From 40d80928806eed2690f8fea70a2d8754dc2ffc64 Mon Sep 17 00:00:00 2001 From: klaus Date: Mon, 20 Apr 2026 19:05:25 +0900 Subject: [PATCH] =?UTF-8?q?chore(ads):=20DARO=20=EA=B4=91=EA=B3=A0=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=EB=A5=BC=20=EB=B0=98=EC=98=81=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 - app/build.gradle | 22 ---- app/proguard-rules.pro | 52 -------- .../co/vividnext/sodalive/app/SodaLiveApp.kt | 14 --- .../sodalive/live/room/LiveRoomActivity.kt | 119 ------------------ .../live/room/LiveRoomDaroLightPopupPolicy.kt | 9 -- .../sodalive/live/room/LiveRoomViewModel.kt | 22 ---- .../sodalive/mypage/MyPageFragment.kt | 78 ------------ app/src/main/res/layout/fragment_my.xml | 8 -- .../room/LiveRoomDaroLightPopupPolicyTest.kt | 52 -------- build.gradle | 1 - docs/20260420_DARO광고제거.md | 40 ++++++ docs/20260420_Daro광고기본세팅.md | 28 ----- docs/20260420_마이페이지배너광고추가.md | 44 ------- docs/20260420_무료라이브라이트팝업광고적용.md | 44 ------- 15 files changed, 40 insertions(+), 497 deletions(-) delete mode 100644 app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomDaroLightPopupPolicy.kt delete mode 100644 app/src/test/java/kr/co/vividnext/sodalive/live/room/LiveRoomDaroLightPopupPolicyTest.kt create mode 100644 docs/20260420_DARO광고제거.md delete mode 100644 docs/20260420_Daro광고기본세팅.md delete mode 100644 docs/20260420_마이페이지배너광고추가.md delete mode 100644 docs/20260420_무료라이브라이트팝업광고적용.md diff --git a/.gitignore b/.gitignore index 38331aba..8533db5b 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/app/build.gradle b/app/build.gradle index 57aa29ab..d11ea475 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 0cefb1e6..e519e7e1 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -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 diff --git a/app/src/main/java/kr/co/vividnext/sodalive/app/SodaLiveApp.kt b/app/src/main/java/kr/co/vividnext/sodalive/app/SodaLiveApp.kt index 4c4816f5..e03f5d15 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/app/SodaLiveApp.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/app/SodaLiveApp.kt @@ -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 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 2be5ac9e..81715e73 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 @@ -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(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(ActivityLiveRoomB override fun onDestroy() { // 액티비티 종료 전에 강제 음소거 상태를 원복한다. clearCapturePrivacyMuteState() - clearDaroLightPopupLoader() - clearDaroLightPopupAd() cropper.cleanup() hideKeyboard { viewModel.quitRoom(roomId) { @@ -1315,7 +1299,6 @@ class LiveRoomActivity : BaseActivity(ActivityLiveRoomB isCaptureRecordingAvailable = response.isCaptureRecordingAvailable syncRoomRoleState(response) - requestDaroLightPopupIfEligible() syncCaptureSecurityPolicy() binding.tvChatFreezeSwitch.visibility = if (isHost) { View.VISIBLE @@ -4293,111 +4276,9 @@ class LiveRoomActivity : BaseActivity(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 } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomDaroLightPopupPolicy.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomDaroLightPopupPolicy.kt deleted file mode 100644 index c5537587..00000000 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomDaroLightPopupPolicy.kt +++ /dev/null @@ -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 -} 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 f91e6dc5..1c790a86 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 @@ -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() } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt index 3972a385..0d274ebd 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageFragment.kt @@ -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::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::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::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() { diff --git a/app/src/main/res/layout/fragment_my.xml b/app/src/main/res/layout/fragment_my.xml index 814ee7f0..717a4097 100644 --- a/app/src/main/res/layout/fragment_my.xml +++ b/app/src/main/res/layout/fragment_my.xml @@ -347,14 +347,6 @@ android:paddingHorizontal="24dp" /> - - diff --git a/app/src/test/java/kr/co/vividnext/sodalive/live/room/LiveRoomDaroLightPopupPolicyTest.kt b/app/src/test/java/kr/co/vividnext/sodalive/live/room/LiveRoomDaroLightPopupPolicyTest.kt deleted file mode 100644 index a4c70101..00000000 --- a/app/src/test/java/kr/co/vividnext/sodalive/live/room/LiveRoomDaroLightPopupPolicyTest.kt +++ /dev/null @@ -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 - ) - ) - } -} diff --git a/build.gradle b/build.gradle index 5aa2fa14..75aee422 100644 --- a/build.gradle +++ b/build.gradle @@ -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' } } diff --git a/docs/20260420_DARO광고제거.md b/docs/20260420_DARO광고제거.md new file mode 100644 index 00000000..c047b2ab --- /dev/null +++ b/docs/20260420_DARO광고제거.md @@ -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` 실행 불가 확인 diff --git a/docs/20260420_Daro광고기본세팅.md b/docs/20260420_Daro광고기본세팅.md deleted file mode 100644 index 143bc610..00000000 --- a/docs/20260420_Daro광고기본세팅.md +++ /dev/null @@ -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` 실행 불가 확인 diff --git a/docs/20260420_마이페이지배너광고추가.md b/docs/20260420_마이페이지배너광고추가.md deleted file mode 100644 index 72a8fdae..00000000 --- a/docs/20260420_마이페이지배너광고추가.md +++ /dev/null @@ -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를 통과했다. diff --git a/docs/20260420_무료라이브라이트팝업광고적용.md b/docs/20260420_무료라이브라이트팝업광고적용.md deleted file mode 100644 index 08a5404f..00000000 --- a/docs/20260420_무료라이브라이트팝업광고적용.md +++ /dev/null @@ -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] 상태다.