diff --git a/app/build.gradle b/app/build.gradle index 3719d61e..88682307 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,8 +35,8 @@ android { applicationId "kr.co.vividnext.sodalive" minSdk 23 targetSdk 34 - versionCode 165 - versionName "1.36.0" + versionCode 167 + versionName "1.37.0" } buildTypes { diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index ea3a4687..1afcbf88 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -237,3 +237,5 @@ -dontwarn org.bouncycastle.jsse.** -dontwarn org.conscrypt.* -dontwarn org.openjsse.** + +-keep interface kr.co.vividnext.sodalive.tracking.UserEventApi diff --git a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt index 94cf9db9..65f728b4 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt @@ -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.tracking.AdTrackingApi 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.UserRepository 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(), AuthApi::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(), LiveApi::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 { LiveTagViewModel(get()) } viewModel { LiveRoomEditViewModel(get()) } - viewModel { LiveRoomViewModel(get(), get(), get(), get()) } + viewModel { LiveRoomViewModel(get(), get(), get(), get(), get()) } viewModel { LiveRoomDonationMessageViewModel(get()) } viewModel { ExplorerViewModel(get()) } viewModel { UserProfileViewModel(get(), get(), get()) } @@ -369,6 +372,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { factory { OriginalAudioDramaContentAllRepository(get()) } factory { AdTrackingRepository(get()) } factory { SearchRepository(get()) } + factory { UserEventRepository(get()) } } private val moduleList = listOf( diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt index fd37c9c9..5e3386d6 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt @@ -58,6 +58,9 @@ import io.agora.rtm.RtmChannelMember import io.agora.rtm.RtmClientListener import io.agora.rtm.RtmMessage 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 kr.co.vividnext.sodalive.R 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.settings.notification.MemberRole import org.koin.android.ext.android.inject +import java.util.concurrent.TimeUnit import java.util.regex.Pattern import kotlin.random.Random @@ -1888,6 +1892,7 @@ class LiveRoomActivity : BaseActivity(ActivityLiveRoomB startNoChatting() } setHeartButtonPosition() + startPeriodicPlaybackValidation() }, rtmChannelJoinFail = { agoraConnectFail() @@ -2303,6 +2308,24 @@ class LiveRoomActivity : BaseActivity(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 { private const val NO_CHATTING_TIME = 180L } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomViewModel.kt index 109f4534..905804a6 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomViewModel.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomViewModel.kt @@ -6,10 +6,12 @@ import androidx.lifecycle.MutableLiveData import com.google.gson.Gson import com.orhanobut.logger.Logger import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.schedulers.Schedulers import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock 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.Utils 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.ReportRequest 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 okhttp3.MediaType.Companion.toMediaType import okhttp3.MultipartBody @@ -40,7 +44,8 @@ class LiveRoomViewModel( private val repository: LiveRepository, private val userRepository: UserRepository, private val reportRepository: ReportRepository, - private val rouletteRepository: RouletteRepository + private val rouletteRepository: RouletteRepository, + private val userEventRepository: UserEventRepository ) : BaseViewModel() { private val _roomInfoLiveData = MutableLiveData() val roomInfoLiveData: LiveData @@ -1124,6 +1129,13 @@ class LiveRoomViewModel( ) } + fun trackLiveContinuousListen30(): Single> { + return userEventRepository.trackEvent( + eventType = UserEventType.LIVE_CONTINUOUS_LISTEN_30, + token = "Bearer ${SharedPreferenceManager.token}" + ) + } + private fun calculatePercentages(options: List): List { val updatedOptions = options.map { option -> RoulettePreviewItem( diff --git a/app/src/main/java/kr/co/vividnext/sodalive/tracking/UserEventApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/tracking/UserEventApi.kt new file mode 100644 index 00000000..5e9b9bf0 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/tracking/UserEventApi.kt @@ -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> +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/tracking/UserEventRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/tracking/UserEventRepository.kt new file mode 100644 index 00000000..3373e3b6 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/tracking/UserEventRepository.kt @@ -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> { + return api.trackEvent( + request = UserEventRequest(actionType = eventType), + authHeader = token + ) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/tracking/UserEventRequest.kt b/app/src/main/java/kr/co/vividnext/sodalive/tracking/UserEventRequest.kt new file mode 100644 index 00000000..dca63c6a --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/tracking/UserEventRequest.kt @@ -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 +}