korlibs / korlibs/korge-monetization

When publishing on PlayConsole show error

Open
#3 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Kotlin
Stars
0
Forks
2
PR merge metrics
No merged PRs in 30d

Description

The latest working release (1144 (1.1.1)) contains SDK with critical bugs
October 3, 2023 23:43
androidx.fragment:fragment:1.0.0
This SDK version has been marked as deprecated. We recommend updating the SDK to the latest version (1.1.0+).

com.google.android.gms:play-services-ads-lite:19.8.0
A message from the developer is attached to this SDK version:

This version is scheduled to sunset on June 30th 2023. See https://developers.google.com/admob/android/deprecation for more information.

From January 2, 2024 at 00:00 (UTC), you will not be able to publish new versions of the application that use this SDK.

com.google.android.gms:play-services-ads:
19.8.0 A message from the developer is attached to this SDK version:

This version is scheduled to sunset on June 30th 2023. See https://developers.google.com/admob/android/deprecation for more information.

From January 2, 2024 at 00:00 (UTC), you will not be able to publish new versions of the application that use this SDK.

I try my self migrate from 16.0.0 to 19.8.0, but it seems need to migrate 21.0.0 or 22.

migrated for 21.0, only InterstitialAd and Banner, may be need for some one

```package korlibs.korge.admob

import android.annotation.SuppressLint
import android.app.*
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.view.*
import android.widget.*
import com.google.android.gms.ads.*
import com.google.android.gms.ads.interstitial.*
import korlibs.io.async.*
import korlibs.io.lang.CancellableGroup
import korlibs.io.lang.close
import korlibs.korge.view.Views
import korlibs.render.gameWindowAndroidContext
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ReceiveChannel

fun Activity.prepareRootRelativeLayout(): RelativeLayout {
val activity = this
val initialRootView = activity.window.decorView.findViewById(android.R.id.content).let {
if (it is FrameLayout) it.getChildAt(0) else it
}
val rootView = initialRootView as? RelativeLayout? ?: RelativeLayout(activity).also { newRelativeLayout ->
runOnUiThread {
initialRootView.removeFromParent()
activity.setContentView(newRelativeLayout)
newRelativeLayout.addView(initialRootView, ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT))
}
}
return rootView
}

actual fun AdmobCreate(views: Views, testing: Boolean): Admob {
val activity = views.gameWindow.gameWindowAndroidContext as Activity
val rootRelativeLayout = activity.prepareRootRelativeLayout()
return AdmobAndroid(views, activity, testing, rootRelativeLayout)
}

private fun runAndroidOnUiThread(context: Context? = null, block: () -> Unit) {
//Looper.prepare()
Handler(context?.mainLooper ?: Looper.getMainLooper()).post(block)
}

@SuppressLint("MissingPermission")
private class AdmobAndroid(
views: Views,
val activity: Activity,
val testing: Boolean,
val rootView: RelativeLayout
) : Admob(views) {

interface BaseListener {
val views: Views
fun Signal.sendInContext(value: T) {
val signal = this
views.launchImmediately {
signal.invoke(value)
}
}
}

class SignalRewardedVideoListener(override val views: Views) : BaseListener { //, RewardedVideoAdListener
val onRewardedVideoAdClosed = Signal()
val onRewardedVideoAdLeftApplication = Signal()
val onRewardedVideoAdLoaded = Signal()
val onRewardedVideoAdOpened = Signal()
val onRewardedVideoCompleted = Signal()
val onRewarded = Signal()
val onRewardedVideoStarted = Signal()
val onRewardedVideoAdFailedToLoad = Signal()

// override fun onRewardedVideoAdClosed() = onRewardedVideoAdClosed.sendInContext(Unit)
// override fun onRewardedVideoAdLeftApplication() = onRewardedVideoAdLeftApplication.sendInContext(Unit)
// override fun onRewardedVideoAdLoaded() = onRewardedVideoAdLoaded.sendInContext(Unit)
// override fun onRewardedVideoAdOpened() = onRewardedVideoAdOpened.sendInContext(Unit)
// override fun onRewardedVideoCompleted() = onRewardedVideoCompleted.sendInContext(Unit)
// override fun onRewardedVideoStarted() = onRewardedVideoStarted.sendInContext(Unit)
// override fun onRewarded(rewardItem: RewardItem) = onRewarded.sendInContext(rewardItem.let { Admob.Reward(it.type, it.amount) })
// override fun onRewardedVideoAdFailedToLoad(p0: Int) = onRewardedVideoAdFailedToLoad.sendInContext(p0)

fun createChannel(): ReceiveChannel {
val channel = Channel()
val group = CancellableGroup()
group += onRewardedVideoAdClosed { channel.trySend(RewardvideoEvent.REWARDED_VIDEO_AD_CLOSED) }
group += onRewardedVideoAdLeftApplication { channel.trySend(RewardvideoEvent.REWARDED_VIDEO_AD_LEFT_APPLICATION) }
group += onRewardedVideoAdLoaded { channel.trySend(RewardvideoEvent.REWARDED_VIDEO_AD_LOADED) }
group += onRewardedVideoAdOpened { channel.trySend(RewardvideoEvent.REWARDED_VIDEO_AD_OPENED) }
group += onRewardedVideoCompleted { channel.trySend(RewardvideoEvent.REWARDED_VIDEO_COMPLETED) }
group += onRewarded { channel.trySend(RewardvideoEvent.REWARDED(it)) }
group += onRewardedVideoStarted { channel.trySend(RewardvideoEvent.REWARDED_VIDEO_STARTED) }
group += onRewardedVideoAdFailedToLoad { channel.trySend(RewardvideoEvent.REWARDED_VIDEO_AD_FAILED_TO_LOAD) }
channel.invokeOnClose {
group.close()
}
return channel
}
}

class SignalAdListener(override val views: Views) : BaseListener, AdListener() {
val onInterstitialAdImpression = Signal()
val onInterstitialAdLeftApplication = Signal()
val onAdClicked = Signal()
val onAdFailedToLoad = Signal()
val onAdClosed = Signal()
val onAdOpened = Signal()
val onAdLoaded = Signal()

override fun onAdImpression() = onInterstitialAdImpression.sendInContext(Unit)
// override fun onAdLeftApplication() = onInterstitialAdLeftApplication.sendInContext(Unit)
override fun onAdClicked() = onAdClicked.sendInContext(Unit)
// override fun onAdFailedToLoad(p0: Int) = onAdFailedToLoad.sendInContext(p0)
override fun onAdFailedToLoad(p0: LoadAdError) {
onAdFailedToLoad.sendInContext(p0.code)
super.onAdFailedToLoad(p0)
}
override fun onAdClosed() = onAdClosed.sendInContext(Unit)
override fun onAdOpened() = onAdOpened.sendInContext(Unit)
override fun onAdLoaded() = onAdLoaded.sendInContext(Unit)

fun createChannel(): ReceiveChannel {
val channel = Channel()
val group = CancellableGroup()
group += onInterstitialAdImpression { channel.trySend(InterstitialEvent.INTERSTITIAL_AD_IMPRESSION) }
group += onInterstitialAdLeftApplication { channel.trySend(InterstitialEvent.INTERSTITIAL_AD_LEFT_APPLICATION) }
group += onAdClicked { channel.trySend(AdEvent.AD_CLICKED) }
group += onAdFailedToLoad { channel.trySend(AdEvent.AD_FAILED_TO_LOAD(it)) }
group += onAdClosed { channel.trySend(AdEvent.AD_CLOSED) }
group += onAdOpened { channel.trySend(AdEvent.AD_OPENED) }
group += onAdLoaded { channel.trySend(AdEvent.AD_LOADED) }
channel.invokeOnClose { group.close() }
return channel
}
}

val interstitialListener = SignalAdListener(views)
var interstitial: InterstitialAd? = null
private var adIsLoading: Boolean = false

// val rewardVideoListener = SignalRewardedVideoListener(views)
// val rewardVideo by lazy { MobileAds.getRewardedVideoAdInstance(activity).apply {
// rewardedVideoAdListener = rewardVideoListener
// } }

val adViewListener = SignalAdListener(views)
private var adView:AdView? = null
private var bannerAtTop = false

fun Admob.Config.toAdRequest(): AdRequest {
val builder = AdRequest.Builder()
if (this.keywords != null) {
for (keyword in keywords) builder.addKeyword(keyword)
}
// if (this.forChild != null) builder.tagForChildDirectedTreatment(forChild)
return builder.build()
}

override suspend fun available() = true

override suspend fun bannerPrepare(config: Admob.Config) {
this.bannerAtTop = config.bannerAtTop
runOnUiThread {
if(adView == null) {
adView = AdView(activity).apply {
adListener = adViewListener
adUnitId = if (testing) "ca-app-pub-3940256099942544/6300978111" else config.id
setAdSize(when (config.size) {
Size.BANNER -> AdSize.BANNER
Size.IAB_BANNER -> AdSize.BANNER
Size.IAB_LEADERBOARD -> AdSize.BANNER
Size.IAB_MRECT -> AdSize.BANNER
Size.LARGE_BANNER -> AdSize.LARGE_BANNER
Size.SMART_BANNER -> AdSize.SMART_BANNER
Size.FLUID -> AdSize.FLUID
})
}
}
adView?.loadAd(config.toAdRequest())
}
}

override suspend fun bannerShow() {
runOnUiThread {
if (adView != null && adView!!.parent != rootView) {
adView?.removeFromParent()
rootView.addView(
adView,
RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT).apply {
addRule(RelativeLayout.CENTER_HORIZONTAL)
addRule(if (bannerAtTop) RelativeLayout.ALIGN_PARENT_TOP else RelativeLayout.ALIGN_PARENT_BOTTOM)
setMargins(0, 70, 0, 0)
}
)
}
adView?.visibility = View.VISIBLE
}
//activity.view
}

override suspend fun bannerHide() {
runOnUiThread {
adView?.visibility = View.INVISIBLE
}
}

private fun loadAd() {
}

override suspend fun interstitialPrepare(config: Admob.Config) {
runOnUiThread {
val adUnitId = if (testing) "ca-app-pub-3940256099942544/1033173712" else config.id

// Request a new ad if one isn't already loaded.
if (adIsLoading || interstitial != null) {
return@runOnUiThread
}
adIsLoading = true

val adRequest = AdRequest.Builder().build()

InterstitialAd.load(
activity,
adUnitId,
adRequest,
object : InterstitialAdLoadCallback() {
override fun onAdFailedToLoad(adError: LoadAdError) {
interstitial = null
adIsLoading = false
val error =
"domain: ${adError.domain}, code: ${adError.code}, " + "message: ${adError.message}"
}

override fun onAdLoaded(ad: InterstitialAd) {
interstitial = ad
adIsLoading = false
}
}
)

// interstitial.loadAd(config.toAdRequest())
}
}

private fun showInterstitial() {
if (interstitial != null) {
interstitial?.fullScreenContentCallback =
object : FullScreenContentCallback() {
override fun onAdDismissedFullScreenContent() {
// Don't forget to set the ad reference to null so you
// don't show the ad a second time.
interstitial = null
}

override fun onAdFailedToShowFullScreenContent(adError: AdError) {
// Don't forget to set the ad reference to null so you
// don't show the ad a second time.
interstitial = null
}

override fun onAdShowedFullScreenContent() {
// Called when ad is dismissed.
}
}
interstitial?.show(activity)
} else {
loadAd()
}
}

override suspend fun interstitialIsLoaded(): Boolean = runOnUiThread { interstitial != null }

override suspend fun interstitialShowAndWait(): Boolean {
val closeables = CancellableGroup()
try {
val result = mapOf(
interstitialListener.onAdClosed to false,
interstitialListener.onAdClicked to true
).executeAndWaitAnySignal {
runOnUiThread {
showInterstitial()
}
}
return result
} finally {
closeables.close()
}
}

override suspend fun rewardvideoPrepare(config: Admob.Config) {
runOnUiThread {
// rewardVideo.userId = config.userId
// if (config.immersiveMode != null) {
// rewardVideo.setImmersiveMode(config.immersiveMode)
// }
// rewardVideo.loadAd(
// if (testing) "ca-app-pub-3940256099942544/5224354917" else config.id,
// config.toAdRequest()
// )
}
}

override suspend fun rewardvideoIsLoaded(): Boolean = runOnUiThread {
// rewardVideo.isLoaded
false
}

override suspend fun rewardvideoShowAndWait(): Boolean {
// rewardVideoListener.onRewardedVideoAdClosed.executeAndWaitSignal {
// runOnUiThread {
// rewardVideo.show()
// }
// }
return true
}

override suspend fun bannerEvents(): ReceiveChannel = adViewListener.createChannel()
// override suspend fun rewardvideoEvents(): ReceiveChannel = rewardVideoListener.createChannel()
override suspend fun interstitialEvents(): ReceiveChannel = interstitialListener.createChannel()

private suspend fun runOnUiThread(block: () -> T): T {
val result = CompletableDeferred()
activity.runOnUiThread {
result.complete(block())
}
return result.await()
}
}

private suspend inline fun Map, T>.executeAndWaitAnySignal(callback: () -> Unit): T {
val deferred = CompletableDeferred()
val closeables = this.map { pair -> pair.key.once { deferred.complete(pair.value) } }
try {
callback()
return deferred.await()
} finally {
closeables.close()
}
}

private suspend inline fun Iterable>.executeAndWaitAnySignal(callback: () -> Unit): Pair, T> {
val deferred = CompletableDeferred, T>>()
val closeables = this.map { signal -> signal.once { deferred.complete(signal to it) } }
try {
callback()
return deferred.await()
} finally {
closeables.close()
}
}

private suspend inline fun Signal.executeAndWaitSignal(callback: () -> Unit): T {
val deferred = CompletableDeferred()
val closeable = this.once { deferred.complete(it) }
try {
callback()
return deferred.await()
} finally {
closeable.close()
}
}

private fun View.removeFromParent() {
(parent as? ViewGroup?)?.removeView(this)
}

```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by reviewing the pasted AdmobAndroid implementation, especially AdmobCreate, bannerPrepare, and interstitialPrepare, alongside the Google Mobile Ads SDK versions described in the issue. Confirm which dependency and API versions the project currently uses, then verify the resulting Android release can be published without the Play Console SDK warning.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin
Domain
mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.