chore(ads): DARO 광고 제거를 반영한다
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -312,10 +312,6 @@ fabric.properties
|
||||
!/gradle/wrapper/gradle-wrapper.jar
|
||||
app/debug/
|
||||
app/release/
|
||||
app/daro-key.txt
|
||||
app/android-daro-key.txt
|
||||
app/src/**/daro-key.txt
|
||||
app/src/**/android-daro-key.txt
|
||||
|
||||
.junie/
|
||||
.kiro/
|
||||
|
||||
@@ -1,21 +1,5 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
buildscript {
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file('local.properties')
|
||||
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withInputStream { stream ->
|
||||
localProperties.load(stream)
|
||||
}
|
||||
}
|
||||
|
||||
ext.daroAppKey = project.findProperty('daroAppKey')
|
||||
?: localProperties.getProperty('daroAppKey')
|
||||
?: System.getenv('DARO_APP_KEY')
|
||||
?: ''
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
@@ -27,7 +11,6 @@ plugins {
|
||||
id 'org.jlleitschuh.gradle.ktlint'
|
||||
|
||||
id 'com.google.firebase.crashlytics'
|
||||
id 'so.daro.a'
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -145,7 +128,6 @@ android {
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
coreLibraryDesugaringEnabled true
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
@@ -267,10 +249,6 @@ dependencies {
|
||||
exclude group: "org.jetbrains.kotlin", module: "kotlin-android-extensions-runtime"
|
||||
}
|
||||
|
||||
// Daro
|
||||
implementation 'so.daro:daro-a:1.5.3'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
||||
|
||||
// ----- Test dependencies -----
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.mockito:mockito-core:5.20.0'
|
||||
|
||||
52
app/proguard-rules.pro
vendored
52
app/proguard-rules.pro
vendored
@@ -245,55 +245,3 @@
|
||||
-keep interface com.yalantis.ucrop** { *; }
|
||||
|
||||
-dontwarn com.linecorp.linesdk.BR
|
||||
|
||||
# Daro Android SDK (Non-Reward)
|
||||
|
||||
# Pangle (ByteDance)
|
||||
-keep class com.bytedance.sdk.** { *; }
|
||||
|
||||
# Amazon APS
|
||||
-keep class com.amazon.** { *; }
|
||||
-keep public class com.google.android.gms.ads.** { public *; }
|
||||
-keep class com.iabtcf.** { *; }
|
||||
|
||||
# IronSource
|
||||
-keepclassmembers class * implements android.os.Parcelable {
|
||||
public static final android.os.Parcelable$Creator *;
|
||||
}
|
||||
-keep class com.ironsource.adapters.** { *; }
|
||||
-dontwarn com.ironsource.**
|
||||
-dontwarn com.ironsource.adapters.**
|
||||
-keepclassmembers class com.ironsource.** { public *; }
|
||||
-keep public class com.ironsource.**
|
||||
-keep class com.ironsource.adapters.** { *; }
|
||||
|
||||
# IronSource - AppLovin integration
|
||||
-keepclassmembers class com.applovin.sdk.AppLovinSdk { static *; }
|
||||
-keep public interface com.applovin.sdk.** { *; }
|
||||
-keep public interface com.applovin.adview.** { *; }
|
||||
-keep public interface com.applovin.mediation.** { *; }
|
||||
-keep public interface com.applovin.communicator.** { *; }
|
||||
|
||||
# IronSource - AndroidX
|
||||
-keep class androidx.localbroadcastmanager.content.LocalBroadcastManager { *; }
|
||||
-keep class androidx.recyclerview.widget.RecyclerView { *; }
|
||||
-keep class androidx.recyclerview.widget.RecyclerView$OnScrollListener { *; }
|
||||
|
||||
# IronSource - Android
|
||||
-keep class * extends android.app.Activity
|
||||
|
||||
# Retrofit
|
||||
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
|
||||
-keep,allowobfuscation,allowshrinking class retrofit2.Response
|
||||
|
||||
# kotlinx.serialization
|
||||
-dontnote kotlinx.serialization.AnnotationsKt
|
||||
-keepclassmembers class kotlinx.serialization.json.** {
|
||||
*** Companion;
|
||||
}
|
||||
-keepclasseswithmembers class kotlinx.serialization.json.** {
|
||||
kotlinx.serialization.KSerializer serializer(...);
|
||||
}
|
||||
|
||||
# Kotlin Coroutines
|
||||
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
|
||||
|
||||
@@ -11,11 +11,9 @@ import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import com.appsflyer.AppsFlyerLib
|
||||
import com.appsflyer.deeplink.DeepLinkResult
|
||||
import com.facebook.FacebookSdk
|
||||
import droom.daro.a.Daro
|
||||
import com.kakao.sdk.common.KakaoSdk
|
||||
import com.orhanobut.logger.AndroidLogAdapter
|
||||
import com.orhanobut.logger.Logger
|
||||
import droom.daro.SDKConfig
|
||||
import kr.co.vividnext.sodalive.BuildConfig
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomPreferenceManager
|
||||
@@ -54,8 +52,6 @@ class SodaLiveApp : Application(), DefaultLifecycleObserver {
|
||||
setupAppsFlyer()
|
||||
|
||||
setupNotifly()
|
||||
|
||||
setupDaro()
|
||||
}
|
||||
|
||||
private fun isDebuggable(): Boolean {
|
||||
@@ -141,16 +137,6 @@ class SodaLiveApp : Application(), DefaultLifecycleObserver {
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupDaro() {
|
||||
Daro.init(
|
||||
application = this,
|
||||
sdkConfig = SDKConfig.Builder()
|
||||
.setDebugMode(BuildConfig.DEBUG && isDebuggable())
|
||||
.setAppMuted(false)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onStart(owner: LifecycleOwner) {
|
||||
super.onStart(owner)
|
||||
isAppInForeground = true
|
||||
|
||||
@@ -71,15 +71,6 @@ import com.bumptech.glide.request.target.Target
|
||||
import com.google.gson.Gson
|
||||
import com.orbitalsonic.waterwave.WaterWaveView
|
||||
import com.orhanobut.logger.Logger
|
||||
import droom.daro.core.adunit.DaroLightPopupAdUnit
|
||||
import droom.daro.core.listener.DaroLightPopupAdListener
|
||||
import droom.daro.core.listener.DaroLightPopupAdLoaderListener
|
||||
import droom.daro.core.model.DaroAdDisplayFailError
|
||||
import droom.daro.core.model.DaroAdInfo
|
||||
import droom.daro.core.model.DaroAdLoadError
|
||||
import droom.daro.core.model.DaroLightPopupAd
|
||||
import droom.daro.core.model.DaroLightPopupAdOptions
|
||||
import droom.daro.loader.DaroLightPopupAdLoader
|
||||
import io.agora.rtc2.ClientRoleOptions
|
||||
import io.agora.rtc2.IRtcEngineEventHandler
|
||||
import io.agora.rtm.LinkStateEvent
|
||||
@@ -176,11 +167,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
private lateinit var roomInfoEditDialog: LiveRoomInfoEditDialog
|
||||
private lateinit var roomUserProfileDialog: LiveRoomUserProfileDialog
|
||||
|
||||
private var daroLightPopupAdLoader: DaroLightPopupAdLoader? = null
|
||||
private var daroLightPopupAd: DaroLightPopupAd? = null
|
||||
private var hasRequestedDaroLightPopupEligibility = false
|
||||
private var hasAttemptedDaroLightPopup = false
|
||||
|
||||
private var isSpeakerMute = false
|
||||
private var isMicrophoneMute = false
|
||||
private var isSpeaker = false
|
||||
@@ -468,8 +454,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
override fun onDestroy() {
|
||||
// 액티비티 종료 전에 강제 음소거 상태를 원복한다.
|
||||
clearCapturePrivacyMuteState()
|
||||
clearDaroLightPopupLoader()
|
||||
clearDaroLightPopupAd()
|
||||
cropper.cleanup()
|
||||
hideKeyboard {
|
||||
viewModel.quitRoom(roomId) {
|
||||
@@ -1315,7 +1299,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
|
||||
isCaptureRecordingAvailable = response.isCaptureRecordingAvailable
|
||||
syncRoomRoleState(response)
|
||||
requestDaroLightPopupIfEligible()
|
||||
syncCaptureSecurityPolicy()
|
||||
binding.tvChatFreezeSwitch.visibility = if (isHost) {
|
||||
View.VISIBLE
|
||||
@@ -4293,111 +4276,9 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
||||
)
|
||||
}
|
||||
|
||||
private fun requestDaroLightPopupIfEligible() {
|
||||
if (hasRequestedDaroLightPopupEligibility || isHost) {
|
||||
return
|
||||
}
|
||||
|
||||
hasRequestedDaroLightPopupEligibility = true
|
||||
|
||||
if (DARO_LIGHT_POPUP_AD_UNIT_KEY.isBlank()) {
|
||||
Logger.w("Daro light popup skipped because ad unit key is blank.")
|
||||
return
|
||||
}
|
||||
|
||||
viewModel.getRoomPriceForDaroLightPopup(roomId = roomId) { roomPrice ->
|
||||
val resolvedRoomPrice = roomPrice ?: return@getRoomPriceForDaroLightPopup
|
||||
if (
|
||||
!shouldAttemptLiveRoomDaroLightPopup(
|
||||
isHost = isHost,
|
||||
roomPrice = resolvedRoomPrice,
|
||||
hasAttemptedPopup = hasAttemptedDaroLightPopup
|
||||
)
|
||||
) {
|
||||
return@getRoomPriceForDaroLightPopup
|
||||
}
|
||||
|
||||
hasAttemptedDaroLightPopup = true
|
||||
loadDaroLightPopup()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadDaroLightPopup() {
|
||||
clearDaroLightPopupLoader()
|
||||
clearDaroLightPopupAd()
|
||||
|
||||
val adUnit = DaroLightPopupAdUnit(
|
||||
key = DARO_LIGHT_POPUP_AD_UNIT_KEY,
|
||||
placement = DARO_LIGHT_POPUP_PLACEMENT,
|
||||
options = DaroLightPopupAdOptions()
|
||||
)
|
||||
|
||||
daroLightPopupAdLoader = DaroLightPopupAdLoader(
|
||||
context = this,
|
||||
adUnit = adUnit
|
||||
).apply {
|
||||
setListener(object : DaroLightPopupAdLoaderListener {
|
||||
override fun onAdLoadSuccess(ad: DaroLightPopupAd, adInfo: DaroAdInfo) {
|
||||
if (isFinishing || isDestroyed) {
|
||||
ad.destroy()
|
||||
clearDaroLightPopupLoader()
|
||||
return
|
||||
}
|
||||
|
||||
daroLightPopupAd = ad
|
||||
ad.setListener(object : DaroLightPopupAdListener {
|
||||
override fun onAdImpression(adInfo: DaroAdInfo) = Unit
|
||||
|
||||
override fun onAdClicked(adInfo: DaroAdInfo) = Unit
|
||||
|
||||
override fun onShown(adInfo: DaroAdInfo) = Unit
|
||||
|
||||
override fun onFailedToShow(
|
||||
adInfo: DaroAdInfo,
|
||||
error: DaroAdDisplayFailError
|
||||
) {
|
||||
Logger.w(
|
||||
"Daro light popup failed to show. message=${error.message}"
|
||||
)
|
||||
clearDaroLightPopupLoader()
|
||||
clearDaroLightPopupAd()
|
||||
}
|
||||
|
||||
override fun onDismiss(adInfo: DaroAdInfo) {
|
||||
clearDaroLightPopupLoader()
|
||||
clearDaroLightPopupAd()
|
||||
}
|
||||
})
|
||||
ad.show(this@LiveRoomActivity)
|
||||
}
|
||||
|
||||
override fun onAdLoadFail(err: DaroAdLoadError) {
|
||||
Logger.w(
|
||||
"Daro light popup load failed. code=${err.code}, message=${err.message}"
|
||||
)
|
||||
clearDaroLightPopupLoader()
|
||||
}
|
||||
})
|
||||
load()
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearDaroLightPopupLoader() {
|
||||
val adLoader = daroLightPopupAdLoader ?: return
|
||||
daroLightPopupAdLoader = null
|
||||
adLoader.destroy()
|
||||
}
|
||||
|
||||
private fun clearDaroLightPopupAd() {
|
||||
val ad = daroLightPopupAd ?: return
|
||||
daroLightPopupAd = null
|
||||
ad.destroy()
|
||||
}
|
||||
// endregion
|
||||
|
||||
companion object {
|
||||
private const val DARO_LIGHT_POPUP_AD_UNIT_KEY = "59082e9e-de1b-4f5d-bbc3-8b4124d110d8"
|
||||
private const val DARO_LIGHT_POPUP_PLACEMENT = "LiveRoomFreeListener"
|
||||
private const val NO_CHATTING_TIME = 180L
|
||||
var isForeground: Boolean = false
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package kr.co.vividnext.sodalive.live.room
|
||||
|
||||
internal fun shouldAttemptLiveRoomDaroLightPopup(
|
||||
isHost: Boolean,
|
||||
roomPrice: Int,
|
||||
hasAttemptedPopup: Boolean
|
||||
): Boolean {
|
||||
return !isHost && roomPrice == 0 && !hasAttemptedPopup
|
||||
}
|
||||
@@ -296,28 +296,6 @@ class LiveRoomViewModel(
|
||||
)
|
||||
}
|
||||
|
||||
fun getRoomPriceForDaroLightPopup(roomId: Long, onResult: (Int?) -> Unit) {
|
||||
compositeDisposable.add(
|
||||
repository.getRoomDetail(roomId, token = "Bearer ${SharedPreferenceManager.token}")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success && it.data != null) {
|
||||
onResult(it.data.price)
|
||||
} else {
|
||||
Logger.w("Daro light popup room detail unavailable. roomId=$roomId")
|
||||
onResult(null)
|
||||
}
|
||||
},
|
||||
{ error ->
|
||||
Logger.e(error.message ?: "Daro light popup room detail request failed.")
|
||||
onResult(null)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun isEqualToHostId(memberId: Int): Boolean {
|
||||
return memberId == roomInfoResponse.creatorId.toInt()
|
||||
}
|
||||
|
||||
@@ -15,15 +15,6 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.load
|
||||
import coil.transform.CircleCropTransformation
|
||||
import com.google.gson.Gson
|
||||
import com.orhanobut.logger.Logger
|
||||
import droom.daro.core.adunit.DaroBannerAdUnit
|
||||
import droom.daro.core.model.DaroAdInfo
|
||||
import droom.daro.core.model.DaroAdLoadError
|
||||
import droom.daro.core.model.DaroBannerSize
|
||||
import droom.daro.core.model.DaroViewAd
|
||||
import droom.daro.view.DaroAdViewListener
|
||||
import droom.daro.view.DaroBannerAdView
|
||||
import kr.co.vividnext.sodalive.BuildConfig
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.audio_content.box.AudioContentBoxActivity
|
||||
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
|
||||
@@ -66,9 +57,6 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
||||
|
||||
companion object {
|
||||
private const val FUNCTION_BUTTON_SPAN_COUNT = 4
|
||||
|
||||
private const val DARO_BANNER_AD_UNIT_KEY = "43df2529-31d8-45f8-a17d-1a760f5bc777"
|
||||
private const val DARO_BANNER_PLACEMENT = "MyPage"
|
||||
}
|
||||
|
||||
private val viewModel: MyPageViewModel by inject()
|
||||
@@ -76,7 +64,6 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
||||
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
private val functionButtonAdapter = FunctionButtonAdapter()
|
||||
private var daroBannerAdView: DaroBannerAdView? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
@@ -86,71 +73,6 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
|
||||
bindData()
|
||||
setupRecentContentSection()
|
||||
setupLatestNotice()
|
||||
setupDaroBottomBanner()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
daroBannerAdView?.destroy()
|
||||
daroBannerAdView = null
|
||||
binding.flDaroBannerContainer.removeAllViews()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun setupDaroBottomBanner() {
|
||||
binding.flDaroBannerContainer.visibility = View.GONE
|
||||
binding.flDaroBannerContainer.removeAllViews()
|
||||
|
||||
val adUnit = DaroBannerAdUnit(
|
||||
key = DARO_BANNER_AD_UNIT_KEY,
|
||||
placement = DARO_BANNER_PLACEMENT,
|
||||
bannerSize = DaroBannerSize.Banner
|
||||
)
|
||||
|
||||
val adView = DaroBannerAdView(
|
||||
context = requireContext(),
|
||||
adUnit = adUnit
|
||||
).apply {
|
||||
setListener(object : DaroAdViewListener {
|
||||
override fun onAdImpression(adInfo: DaroAdInfo) = Unit
|
||||
|
||||
override fun onAdClicked(adInfo: DaroAdInfo) = Unit
|
||||
|
||||
override fun onAdLoadSuccess(ad: DaroViewAd, adInfo: DaroAdInfo) {
|
||||
binding.flDaroBannerContainer.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun onAdLoadFail(err: DaroAdLoadError) {
|
||||
handleDaroBannerLoadFail(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
daroBannerAdView = adView
|
||||
binding.flDaroBannerContainer.addView(adView)
|
||||
adView.loadAd()
|
||||
}
|
||||
|
||||
private fun handleDaroBannerLoadFail(err: DaroAdLoadError) {
|
||||
binding.flDaroBannerContainer.visibility = View.GONE
|
||||
|
||||
Logger.w(
|
||||
"Daro banner load failed. package=${BuildConfig.APPLICATION_ID}, placement=$DARO_BANNER_PLACEMENT"
|
||||
)
|
||||
Logger.w(
|
||||
"Daro banner load failed. code=${err.code}, message=${err.message}"
|
||||
)
|
||||
|
||||
if (err.message.contains("no fill", ignoreCase = true)) {
|
||||
Logger.w(
|
||||
"Daro no fill. Verify app-ads.txt, Live status, registered package=${BuildConfig.APPLICATION_ID}"
|
||||
)
|
||||
|
||||
if (BuildConfig.DEBUG && BuildConfig.APPLICATION_ID.endsWith(".debug")) {
|
||||
Logger.w(
|
||||
"Debug package differs from release. Register ${BuildConfig.APPLICATION_ID} in Daro or test release package."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupLatestNotice() {
|
||||
|
||||
@@ -347,14 +347,6 @@
|
||||
android:paddingHorizontal="24dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Daro bottom banner container -->
|
||||
<FrameLayout
|
||||
android:id="@+id/fl_daro_banner_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package kr.co.vividnext.sodalive.live.room
|
||||
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class LiveRoomDaroLightPopupPolicyTest {
|
||||
|
||||
@Test
|
||||
fun `비방장 무료 라이브에서 아직 시도하지 않았으면 라이트 팝업을 노출한다`() {
|
||||
assertTrue(
|
||||
shouldAttemptLiveRoomDaroLightPopup(
|
||||
isHost = false,
|
||||
roomPrice = 0,
|
||||
hasAttemptedPopup = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `방장이면 무료 라이브여도 라이트 팝업을 노출하지 않는다`() {
|
||||
assertFalse(
|
||||
shouldAttemptLiveRoomDaroLightPopup(
|
||||
isHost = true,
|
||||
roomPrice = 0,
|
||||
hasAttemptedPopup = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `유료 라이브면 비방장이어도 라이트 팝업을 노출하지 않는다`() {
|
||||
assertFalse(
|
||||
shouldAttemptLiveRoomDaroLightPopup(
|
||||
isHost = false,
|
||||
roomPrice = 100,
|
||||
hasAttemptedPopup = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `이미 시도한 액티비티 인스턴스에서는 다시 노출하지 않는다`() {
|
||||
assertFalse(
|
||||
shouldAttemptLiveRoomDaroLightPopup(
|
||||
isHost = false,
|
||||
roomPrice = 0,
|
||||
hasAttemptedPopup = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ buildscript {
|
||||
dependencies {
|
||||
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.6'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'so.daro:daro-plugin:1.0.13'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
40
docs/20260420_DARO광고제거.md
Normal file
40
docs/20260420_DARO광고제거.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 20260420 DARO 광고 제거
|
||||
|
||||
## 작업 체크리스트
|
||||
- [x] DARO 광고 관련 실제 참조 범위를 확정한다.
|
||||
QA: `build.gradle`, `app/build.gradle`, `app/proguard-rules.pro`, `SodaLiveApp.kt`, `MyPageFragment.kt`, `fragment_my.xml`, `LiveRoomActivity.kt`, `LiveRoomViewModel.kt`, `LiveRoomDaroLightPopupPolicy.kt`, `LiveRoomDaroLightPopupPolicyTest.kt`, `.gitignore`, 관련 `docs/*.md`의 DARO 참조 여부를 근거로 설명할 수 있어야 한다.
|
||||
- [x] Gradle/설정 레벨의 DARO 의존성과 키 관련 설정을 제거한다.
|
||||
QA: 루트/plugin/module 의존성, `daroAppKey` 읽기, `.gitignore`의 DARO 키 파일 예외, `app/proguard-rules.pro`의 DARO 전용 규칙이 제거되어야 한다.
|
||||
- [x] 앱 초기화와 광고 노출 UI/로직에서 DARO 코드를 제거한다.
|
||||
QA: `SodaLiveApp`의 SDK 초기화, `MyPageFragment` 배너, `fragment_my.xml` 배너 컨테이너, `LiveRoomActivity` 라이트 팝업 관련 코드가 제거되어야 한다.
|
||||
- [x] DARO 제거 후 불필요해진 보조 코드와 테스트를 정리한다.
|
||||
QA: `LiveRoomViewModel`의 DARO 전용 조회 메서드, `LiveRoomDaroLightPopupPolicy.kt`, `LiveRoomDaroLightPopupPolicyTest.kt`가 정리되어야 한다.
|
||||
- [x] DARO 관련 작업 문서를 정리하고 검증 기록을 남긴다.
|
||||
QA: DARO 기능 추가용 문서(`20260420_Daro광고기본세팅.md`, `20260420_마이페이지배너광고추가.md`, `20260420_무료라이브라이트팝업광고적용.md`)를 제거하고, 의존성 이력 문서(`20260420_BlurTransformation오류수정.md`)는 과거 원인 기록으로 유지한 채 이 문서 하단에 검증 결과를 누적 기록해야 한다.
|
||||
|
||||
## 범위 메모
|
||||
- 요청 해석은 "저장소에 남아 있는 DARO 광고 관련 코드/설정/전용 문서 제거"로 한정한다.
|
||||
- `local.properties`의 `daroAppKey`는 로컬 비추적 환경 설정이므로 이번 저장소 변경 범위에서는 제외한다.
|
||||
- `coreLibraryDesugaringEnabled`, `desugar_jdk_libs`, 외부 저장소 설정은 현재 근거만으로 DARO 전용이라고 단정할 수 없어 유지한다.
|
||||
|
||||
## 검증 계획
|
||||
- `grep`으로 저장소 내 `Daro|daro|DARO` 참조를 재검색해 잔존 항목을 확인한다.
|
||||
- `./gradlew :app:testDebugUnitTest`를 실행해 단위 테스트 회귀를 확인한다.
|
||||
- `./gradlew :app:assembleDebug`를 실행해 앱 빌드 성공을 확인한다.
|
||||
- 필요 시 `git diff`로 DARO 제거 범위가 요청 범위를 넘지 않았는지 수동 확인한다.
|
||||
|
||||
## 검증 기록
|
||||
- 2026-04-20
|
||||
- 무엇: DARO 광고 관련 Gradle 의존성/플러그인, 앱 초기화, 마이페이지 배너, 라이브룸 라이트 팝업, 전용 정책/테스트, 로컬 DARO 키 파일, 관련 기능 문서를 제거했다.
|
||||
- 왜: 저장소에서 더 이상 DARO 광고 SDK와 그 진입 경로가 남지 않도록 요청 범위를 코드/설정/전용 문서 기준으로 정리해야 했기 때문이다.
|
||||
- 어떻게:
|
||||
- 수정 파일: `build.gradle`, `app/build.gradle`, `app/proguard-rules.pro`, `.gitignore`, `app/src/main/java/kr/co/vividnext/sodalive/app/SodaLiveApp.kt`, `app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt`, `app/src/main/res/layout/fragment_my.xml`, `app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt`, `app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomViewModel.kt`, `docs/20260420_DARO광고제거.md`
|
||||
- 삭제 파일: `app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomDaroLightPopupPolicy.kt`, `app/src/test/java/kr/co/vividnext/sodalive/live/room/LiveRoomDaroLightPopupPolicyTest.kt`, `docs/20260420_Daro광고기본세팅.md`, `docs/20260420_마이페이지배너광고추가.md`, `docs/20260420_무료라이브라이트팝업광고적용.md`, `app/daro-key.txt`
|
||||
- 유지 항목: `local.properties`의 `daroAppKey`는 로컬 비추적 설정이라 저장소 변경 대상에서 제외했고, `coreLibraryDesugaringEnabled`, `desugar_jdk_libs`, 외부 저장소 설정은 DARO 전용 근거가 없어 유지했다.
|
||||
- 실행 명령: `grep -R -nE "Daro|daro|DARO"`에 해당하는 저장소 재검색
|
||||
- 결과: 코드/설정 대상(`*.kt`, `*.gradle`, `*.xml`, `*.pro`, `*.gitignore`)에서는 DARO 참조가 0건이었다. 남은 문자열은 `docs/20260420_DARO광고제거.md`와 과거 이력 문서 `docs/20260420_BlurTransformation오류수정.md`뿐이다.
|
||||
- 실행 명령: `./gradlew :app:testDebugUnitTest :app:assembleDebug`
|
||||
- 결과: `BUILD SUCCESSFUL`. 단위 테스트와 debug 빌드가 모두 성공했고, Agora/Appsflyer 관련 기존 경고만 출력됐다.
|
||||
- 실행 명령: `git status --short`
|
||||
- 결과: DARO 제거와 직접 연결된 파일만 수정/삭제된 것을 수동 확인했다.
|
||||
- 진단 도구: Kotlin(`.kt`)용 LSP 서버 미구성으로 `lsp_diagnostics` 실행 불가 확인
|
||||
@@ -1,28 +0,0 @@
|
||||
# 20260420 Daro 광고 기본 세팅
|
||||
|
||||
## 작업 체크리스트
|
||||
- [x] 공식 Daro Android 가이드를 기준으로 `Non-Reward` + Android View 전용 기본 세팅 범위를 확정한다.
|
||||
QA: Compose 전용 의존성/예제는 제외되고, `daro-a`, plugin, key 설정, SDK 초기화, ProGuard 규칙만 반영되어야 한다.
|
||||
- [x] `settings.gradle`, `build.gradle`, `app/build.gradle`에 Daro 저장소/플러그인/SDK 및 desugaring 설정을 추가한다.
|
||||
QA: 공식 문서 버전(`daro-plugin:1.0.13`, `daro-a:1.5.3`)과 minSdk 23 대응 desugaring 설정이 반영되어야 한다.
|
||||
- [x] `SodaLiveApp`에 Daro SDK 초기화를 추가한다.
|
||||
QA: 광고 로드 이전 `Application.onCreate()`에서 `Daro.init(...)`가 호출되어야 한다.
|
||||
- [x] `app/proguard-rules.pro`에 공식 Non-Reward ProGuard/R8 규칙을 반영한다.
|
||||
QA: Daro 문서의 Non-Reward keep/dontwarn 규칙이 파일에 추가되어야 한다.
|
||||
- [x] Daro 키 파일/앱 키의 비밀값 커밋 방지 경로를 보강한다.
|
||||
QA: `app/daro-key.txt`, `app/android-daro-key.txt` 및 flavor/buildType 분기 키 파일이 git 추적 대상에서 제외되어야 한다.
|
||||
- [x] 변경 사항을 빌드로 검증하고 결과를 기록한다.
|
||||
QA: `:app:assembleDebug`, `:app:assembleRelease` 결과와 비밀값 의존 여부를 문서 하단 검증 기록에 남겨야 한다.
|
||||
|
||||
## 검증 기록
|
||||
- 2026-04-20
|
||||
- 무엇: Daro Android 기본 세팅(Non-Reward, Android View 전용)을 위해 저장소/플러그인/SDK/desugaring 설정, `SodaLiveApp` 초기화, Non-Reward ProGuard 규칙, 키 파일 ignore 경로를 반영했다.
|
||||
- 왜: 공식 `get-started` 가이드 기준으로 Compose 의존성을 제외한 Android View 앱용 기본 연동과 난독화 설정이 필요했다.
|
||||
- 어떻게:
|
||||
- 수정 파일: `settings.gradle`, `build.gradle`, `app/build.gradle`, `.gitignore`, `app/src/main/java/kr/co/vividnext/sodalive/app/SodaLiveApp.kt`, `app/proguard-rules.pro`
|
||||
- 실행 명령: `./gradlew :app:assembleDebug`
|
||||
- 결과: `DARO` 플러그인 적용과 의존성 해상도는 진행됐지만, 로컬 키 파일 `app/daro-key.txt`를 읽는 과정에서 `Tag mismatch!`가 발생해 `설정 파일이 유효하지 않습니다`로 실패했다.
|
||||
- 실행 명령: `./gradlew :app:assembleRelease`
|
||||
- 결과: release만 요청해도 플러그인 설정 단계에서 `debug` variant 키 검증이 먼저 수행됐고, 동일하게 `app/daro-key.txt`의 `Tag mismatch!`로 실패했다. 따라서 현재 차단점은 ProGuard/R8 규칙이 아니라 로컬 Daro 키 파일/앱 키 정합성이다.
|
||||
- 메모: 최신 문서는 `android-daro-key.txt`를 안내하지만, 실제 플러그인은 레거시 `app/daro-key.txt`도 읽고 있었다. 현재 로컬에 존재하는 키 파일과 `daroAppKey`가 서로 맞지 않거나 파일 자체가 유효하지 않아 빌드 검증이 차단됐다.
|
||||
- 진단 도구: Kotlin(`.kt`)용 LSP 서버 미구성으로 `lsp_diagnostics` 실행 불가 확인
|
||||
@@ -1,44 +0,0 @@
|
||||
# 20260420 마이페이지 배너 광고 추가
|
||||
|
||||
## 작업 체크리스트
|
||||
- [x] Daro 공식 Android 배너 가이드와 현재 프로젝트의 광고 기본 세팅 상태를 확인한다.
|
||||
QA: 배너 뷰 타입, 필수 값, 기존 SDK 초기화/플러그인 상태를 근거 파일로 설명할 수 있어야 한다.
|
||||
- [x] `fragment_my.xml` 최하단에 배너 광고 영역을 추가한다.
|
||||
QA: 기존 MyPage 콘텐츠 하단에 배너 광고 뷰가 배치되고, 화면 레이아웃을 깨지 않아야 한다.
|
||||
- [x] `MyPageFragment.kt`에 배너 광고 로드/정리 로직과 임시 설정값 위치 안내를 추가한다.
|
||||
QA: 사용자가 교체해야 하는 값이 코드에서 명확히 드러나고, Fragment 생명주기에 맞는 정리 처리가 있어야 한다.
|
||||
- [x] 변경 사항을 진단하고 필요한 Gradle 검증을 수행한 뒤 결과를 기록한다.
|
||||
QA: 변경 파일 진단 결과와 실행한 검증 명령/결과가 문서 하단에 누적 기록되어야 한다.
|
||||
|
||||
## 임시 설정값 위치
|
||||
- `app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt`
|
||||
- `DARO_BANNER_AD_UNIT_KEY`
|
||||
- `DARO_BANNER_PLACEMENT`
|
||||
- 위 두 상수를 실제 Daro 배너 값으로 교체하면 된다.
|
||||
|
||||
## 검증 기록
|
||||
- 2026-04-20
|
||||
- 무엇: `fragment_my.xml` 최하단에 Daro 배너 컨테이너를 추가하고, `MyPageFragment`에서 `DaroBannerAdView`를 생성해 로드/정리하도록 구현했다.
|
||||
- 왜: 공식 Daro Android 배너 가이드가 XML 커스텀 태그 대신 컨테이너 뷰 + 코드 생성 방식의 `DaroBannerAdView` 사용을 요구했고, 요청 위치가 MyPage 최하단이었기 때문이다.
|
||||
- 어떻게:
|
||||
- 수정 파일: `app/src/main/res/layout/fragment_my.xml`, `app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt`, `docs/20260420_마이페이지배너광고추가.md`
|
||||
- 임시 값 위치: `MyPageFragment.kt`의 `DARO_BANNER_AD_UNIT_KEY`, `DARO_BANNER_PLACEMENT`
|
||||
- 진단 도구: Kotlin/XML LSP 서버 미구성으로 `lsp_diagnostics`는 실행 불가였다.
|
||||
- 실행 명령: `./gradlew :app:assembleDebug`
|
||||
- 결과: 성공. Daro 플러그인 설정과 앱 debug 빌드가 모두 완료됐다.
|
||||
- 실행 명령: `./gradlew :app:ktlintCheck`
|
||||
- 결과: 실패. 이번 변경 파일이 아니라 기존 파일들(`NicknameUpdateViewModel.kt`, `ProfileUpdateActivity.kt`, `RecentContentDao.kt` 등)의 스타일 오류 때문에 `:app:ktlintMainSourceSetCheck`가 실패했다.
|
||||
- 실행 명령: `adb devices`
|
||||
- 결과: 연결된 기기가 없어 실제 화면 수동 확인은 이 환경에서 진행하지 못했다.
|
||||
- 2026-04-20
|
||||
- 무엇: MyPage Daro 배너의 `No fill` 원인을 추가 조사하고, 배너 로드 실패 시 진단 로그가 남도록 `MyPageFragment`를 보강했다.
|
||||
- 왜: 공식 Daro 문서와 SDK 소스 기준으로 `No fill`은 코드 버그보다 `app-ads.txt`, 앱/광고 단위 Live 상태, 등록 패키지 불일치 같은 송출 조건 이슈 가능성이 더 높았고, 기존 코드는 실패를 조용히 숨기고 있었다.
|
||||
- 어떻게:
|
||||
- 수정 파일: `app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt`, `docs/20260420_마이페이지배너광고추가.md`
|
||||
- 조사 근거: Daro 공식 FAQ/배너 가이드, SDK `DaroAdLoader.kt`의 `no fill` 처리 메시지, debug merged manifest의 패키지 `kr.co.vividnext.sodalive.debug`, release merged manifest의 패키지 `kr.co.vividnext.sodalive`
|
||||
- 확인 사항: 저장소 내 `app-ads.txt` 파일은 없었고, debug 빌드는 `applicationIdSuffix '.debug'`로 인해 release 패키지와 다른 앱 ID를 사용한다.
|
||||
- 적용 내용: `No fill` 발생 시 현재 `APPLICATION_ID`, placement, 에러 코드/메시지와 함께 `app-ads.txt`, Live 상태, 등록 패키지 확인이 필요하다는 로그를 남기고, debug 패키지(`.debug`)면 별도 등록 또는 release 패키지 테스트 필요 로그를 추가했다.
|
||||
- 실행 명령: `./gradlew :app:assembleDebug`
|
||||
- 결과: 성공. 진단 로그 추가 후에도 debug 빌드는 정상 완료됐다.
|
||||
- 실행 명령: `./gradlew :app:ktlintCheck`
|
||||
- 결과: 성공. `MyPageFragment.kt`의 로그 문자열 줄 길이를 정리한 뒤 lint를 통과했다.
|
||||
@@ -1,44 +0,0 @@
|
||||
# 20260420 무료 라이브 라이트 팝업 광고 적용
|
||||
|
||||
## 작업 체크리스트
|
||||
- [x] 관련 모듈과 기존 패턴, 무료 라이브 판별값, 광고 연동 조건을 조사한다.
|
||||
QA: `LiveRoomActivity`, `GetRoomInfoResponse`, `GetRoomDetailResponse`, Daro 공식 Light Popup 가이드에서 조건 근거를 확인해야 한다.
|
||||
- [x] 무료 라이브 라이트 팝업 노출 정책 테스트를 추가한다.
|
||||
QA: 비방장 + 무료 + 미시도 조합만 허용되고, 나머지 조합은 모두 차단되어야 한다.
|
||||
- [x] `LiveRoomActivity`와 `LiveRoomViewModel`에 Light Popup 연동을 최소 변경으로 구현한다.
|
||||
QA: 방장이 아니고 무료 라이브일 때만 Daro Light Popup 시도 경로가 존재해야 하며, 같은 액티비티 인스턴스에서 중복 시도되지 않아야 한다.
|
||||
- [x] Ad Unit ID를 추후 입력할 수 있도록 단일 수정 지점을 만든다.
|
||||
QA: 사용자가 수정해야 하는 상수 위치를 코드에서 즉시 확인할 수 있어야 한다.
|
||||
- [x] 변경 사항을 진단하고 관련 Gradle 검증을 수행한 뒤 결과를 기록한다.
|
||||
QA: 관련 단위 테스트와 `:app:assembleDebug` 결과를 문서 하단 검증 기록에 남겨야 한다.
|
||||
|
||||
## 구현 메모
|
||||
- `LiveRoomActivity`의 `GetRoomInfoResponse`에는 `creatorId`는 있지만 `price`/`isPaid`가 없어 무료 여부를 직접 판별할 수 없다.
|
||||
- 무료 여부는 기존 `LiveRepository.getRoomDetail(roomId, token)` 경로의 `GetRoomDetailResponse.price`로 판별한다.
|
||||
- 유료 라이브 입장 여부를 뜻하는 `GetRoomDetailResponse.isPaid`는 무료 판별값이 아니므로 광고 게이트에는 사용하지 않는다.
|
||||
- Daro SDK는 이미 앱 전역에서 초기화되어 있으므로 `LiveRoomActivity`에는 Light Popup 로드/표시 로직만 추가한다.
|
||||
- Ad Unit ID와 placement는 `LiveRoomActivity` companion object의 상수로 두어 사용자가 추후 값만 교체할 수 있게 한다.
|
||||
|
||||
## 검증 기록
|
||||
- 2026-04-20
|
||||
- 무엇: `LiveRoomActivity`에 무료 라이브 비방장 전용 Daro Light Popup을 추가하기 위한 조건, SDK 사용 방식, 최소 수정 범위를 조사했다.
|
||||
- 왜: 현재 액티비티 응답에는 무료/유료 필드가 없어 별도 상세 응답 확인이 필요하고, Daro Light Popup은 공식 클래스/콜백명을 확인한 뒤 연결해야 하기 때문이다.
|
||||
- 어떻게:
|
||||
- 조사 파일: `app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt`, `app/src/main/java/kr/co/vividnext/sodalive/live/room/info/GetRoomInfoResponse.kt`, `app/src/main/java/kr/co/vividnext/sodalive/live/room/detail/GetRoomDetailResponse.kt`, `app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt`, `app/src/main/java/kr/co/vividnext/sodalive/app/SodaLiveApp.kt`
|
||||
- 조사 문서: `https://guide.daro.so/ko/sdk-integration/android/ad-formats/lightpopup`
|
||||
- 확인 결과: 비방장 조건은 `creatorId != SharedPreferenceManager.userId`, 무료 조건은 `GetRoomDetailResponse.price == 0`, SDK 타입은 `DaroLightPopupAdUnit`, `DaroLightPopupAdLoader`, `DaroLightPopupAdListener` 경로로 확정했다.
|
||||
- 2026-04-20
|
||||
- 무엇: `LiveRoomActivity`에 무료 라이브 비방장 전용 Daro Light Popup 시도 경로를 추가하고, Ad Unit ID 입력 위치를 companion object 상수로 고정했다.
|
||||
- 왜: 현재 `GetRoomInfoResponse`만으로는 무료 여부를 알 수 없어 별도 상세 조회가 필요했고, 사용자가 나중에 광고 단위 ID만 안전하게 교체할 수 있어야 했기 때문이다.
|
||||
- 어떻게:
|
||||
- 수정 파일: `app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt`, `app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomViewModel.kt`, `app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomDaroLightPopupPolicy.kt`, `app/src/test/java/kr/co/vividnext/sodalive/live/room/LiveRoomDaroLightPopupPolicyTest.kt`, `docs/20260420_무료라이브라이트팝업광고적용.md`
|
||||
- Ad Unit ID 위치: `app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt`의 `DARO_LIGHT_POPUP_AD_UNIT_KEY`
|
||||
- 실행 명령: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.live.room.LiveRoomDaroLightPopupPolicyTest"`
|
||||
- 결과: 성공. 비방장/무료/중복시도 조건 테스트가 `BUILD SUCCESSFUL`로 통과했다.
|
||||
- 실행 명령: `./gradlew :app:assembleDebug`
|
||||
- 결과: 성공. debug APK가 `BUILD SUCCESSFUL`로 생성됐다.
|
||||
- 실행 명령: `./gradlew :app:ktlintCheck`
|
||||
- 결과: 실패. 이번 변경과 무관한 기존 파일(`SodaLiveApp.kt`, 기존 `LiveRoomActivity.kt` 누적 포맷 위반) 때문에 `:app:ktlintMainSourceSetCheck`가 실패했다.
|
||||
- 진단 도구: Kotlin(`.kt`)용 LSP 서버 미구성으로 변경 Kotlin 파일의 `lsp_diagnostics`는 실행 불가였고, Markdown 문서는 진단 이슈가 없었다.
|
||||
- 실행 명령: `adb devices`, `adb devices -l`, `./gradlew :app:installDebug`
|
||||
- 결과: 장치 연결 상태가 일시적으로 변동됐고 최종적으로 연결된 기기가 없어 `installDebug` 수동 QA를 완료하지 못했다. 따라서 실기기 광고 노출 검증은 [blocked] 상태다.
|
||||
Reference in New Issue
Block a user