Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ad Caching and Loading Enhancements #163

Merged
merged 3 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 41 additions & 23 deletions monetisation/admob/api/admob.api
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public abstract class dev/teogor/ceres/monetisation/admob/formats/Ad : dev/teogo
public abstract fun loadContinuously ()Z
public fun log (Ljava/lang/String;)V
public final fun onListener (Ldev/teogor/ceres/monetisation/admob/formats/AdEvent;)V
public fun reloadExpiredAd ()V
protected final fun setLoading (Z)V
protected final fun setShowing (Z)V
public fun show ()V
Expand All @@ -89,8 +90,8 @@ public abstract class dev/teogor/ceres/monetisation/admob/formats/Ad : dev/teogo
public final class dev/teogor/ceres/monetisation/admob/formats/AdCache {
public static final field $stable I
public static final field INSTANCE Ldev/teogor/ceres/monetisation/admob/formats/AdCache;
public final fun cacheAd (Ljava/lang/String;Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel;)V
public final fun getAd (Ljava/lang/String;)Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel;
public final fun cacheAd (Ljava/lang/String;Ldev/teogor/ceres/monetisation/admob/formats/CachedAd;)V
public final fun getAd (Ljava/lang/String;)Ldev/teogor/ceres/monetisation/admob/formats/CachedAd;
public final fun getAdCount (Ljava/lang/String;)I
public final fun getAds (Ljava/lang/String;I)Ljava/util/List;
public final fun removeAd (Ljava/lang/String;)V
Expand Down Expand Up @@ -213,6 +214,11 @@ public final class dev/teogor/ceres/monetisation/admob/formats/AdType : java/lan
public static fun values ()[Ldev/teogor/ceres/monetisation/admob/formats/AdType;
}

public final class dev/teogor/ceres/monetisation/admob/formats/AdTypeKt {
public static final fun toFriendlyName (Ldev/teogor/ceres/monetisation/admob/formats/AdType;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
public static synthetic fun toFriendlyName$default (Ldev/teogor/ceres/monetisation/admob/formats/AdType;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/String;
}

public abstract class dev/teogor/ceres/monetisation/admob/formats/AppOpenAd : dev/teogor/ceres/monetisation/admob/formats/Ad {
public static final field $stable I
public fun <init> ()V
Expand All @@ -234,66 +240,78 @@ public abstract class dev/teogor/ceres/monetisation/admob/formats/BannerAd : dev
public fun useCache ()Z
}

public abstract class dev/teogor/ceres/monetisation/admob/formats/CacheAdModel {
public abstract class dev/teogor/ceres/monetisation/admob/formats/CachedAd {
public static final field $stable I
public synthetic fun <init> (JLkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun getLoadTime ()J
}

public final class dev/teogor/ceres/monetisation/admob/formats/CacheAdModel$AppOpen : dev/teogor/ceres/monetisation/admob/formats/CacheAdModel {
public final class dev/teogor/ceres/monetisation/admob/formats/CachedAd$AppOpen : dev/teogor/ceres/monetisation/admob/formats/CachedAd {
public static final field $stable I
public fun <init> (Lcom/google/android/gms/ads/appopen/AppOpenAd;)V
public fun <init> (Lcom/google/android/gms/ads/appopen/AppOpenAd;J)V
public final fun component1 ()Lcom/google/android/gms/ads/appopen/AppOpenAd;
public final fun copy (Lcom/google/android/gms/ads/appopen/AppOpenAd;)Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$AppOpen;
public static synthetic fun copy$default (Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$AppOpen;Lcom/google/android/gms/ads/appopen/AppOpenAd;ILjava/lang/Object;)Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$AppOpen;
public final fun component2 ()J
public final fun copy (Lcom/google/android/gms/ads/appopen/AppOpenAd;J)Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$AppOpen;
public static synthetic fun copy$default (Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$AppOpen;Lcom/google/android/gms/ads/appopen/AppOpenAd;JILjava/lang/Object;)Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$AppOpen;
public fun equals (Ljava/lang/Object;)Z
public final fun getAd ()Lcom/google/android/gms/ads/appopen/AppOpenAd;
public fun getLoadTime ()J
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class dev/teogor/ceres/monetisation/admob/formats/CacheAdModel$Interstitial : dev/teogor/ceres/monetisation/admob/formats/CacheAdModel {
public final class dev/teogor/ceres/monetisation/admob/formats/CachedAd$Interstitial : dev/teogor/ceres/monetisation/admob/formats/CachedAd {
public static final field $stable I
public fun <init> (Lcom/google/android/gms/ads/interstitial/InterstitialAd;)V
public fun <init> (Lcom/google/android/gms/ads/interstitial/InterstitialAd;J)V
public final fun component1 ()Lcom/google/android/gms/ads/interstitial/InterstitialAd;
public final fun copy (Lcom/google/android/gms/ads/interstitial/InterstitialAd;)Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$Interstitial;
public static synthetic fun copy$default (Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$Interstitial;Lcom/google/android/gms/ads/interstitial/InterstitialAd;ILjava/lang/Object;)Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$Interstitial;
public final fun component2 ()J
public final fun copy (Lcom/google/android/gms/ads/interstitial/InterstitialAd;J)Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$Interstitial;
public static synthetic fun copy$default (Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$Interstitial;Lcom/google/android/gms/ads/interstitial/InterstitialAd;JILjava/lang/Object;)Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$Interstitial;
public fun equals (Ljava/lang/Object;)Z
public final fun getAd ()Lcom/google/android/gms/ads/interstitial/InterstitialAd;
public fun getLoadTime ()J
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class dev/teogor/ceres/monetisation/admob/formats/CacheAdModel$Native : dev/teogor/ceres/monetisation/admob/formats/CacheAdModel {
public final class dev/teogor/ceres/monetisation/admob/formats/CachedAd$Native : dev/teogor/ceres/monetisation/admob/formats/CachedAd {
public static final field $stable I
public fun <init> (Lcom/google/android/gms/ads/nativead/NativeAd;)V
public fun <init> (Lcom/google/android/gms/ads/nativead/NativeAd;J)V
public final fun component1 ()Lcom/google/android/gms/ads/nativead/NativeAd;
public final fun copy (Lcom/google/android/gms/ads/nativead/NativeAd;)Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$Native;
public static synthetic fun copy$default (Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$Native;Lcom/google/android/gms/ads/nativead/NativeAd;ILjava/lang/Object;)Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$Native;
public final fun component2 ()J
public final fun copy (Lcom/google/android/gms/ads/nativead/NativeAd;J)Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$Native;
public static synthetic fun copy$default (Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$Native;Lcom/google/android/gms/ads/nativead/NativeAd;JILjava/lang/Object;)Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$Native;
public fun equals (Ljava/lang/Object;)Z
public final fun getAd ()Lcom/google/android/gms/ads/nativead/NativeAd;
public fun getLoadTime ()J
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class dev/teogor/ceres/monetisation/admob/formats/CacheAdModel$Rewarded : dev/teogor/ceres/monetisation/admob/formats/CacheAdModel {
public final class dev/teogor/ceres/monetisation/admob/formats/CachedAd$Rewarded : dev/teogor/ceres/monetisation/admob/formats/CachedAd {
public static final field $stable I
public fun <init> (Lcom/google/android/gms/ads/rewarded/RewardedAd;)V
public fun <init> (Lcom/google/android/gms/ads/rewarded/RewardedAd;J)V
public final fun component1 ()Lcom/google/android/gms/ads/rewarded/RewardedAd;
public final fun copy (Lcom/google/android/gms/ads/rewarded/RewardedAd;)Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$Rewarded;
public static synthetic fun copy$default (Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$Rewarded;Lcom/google/android/gms/ads/rewarded/RewardedAd;ILjava/lang/Object;)Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$Rewarded;
public final fun component2 ()J
public final fun copy (Lcom/google/android/gms/ads/rewarded/RewardedAd;J)Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$Rewarded;
public static synthetic fun copy$default (Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$Rewarded;Lcom/google/android/gms/ads/rewarded/RewardedAd;JILjava/lang/Object;)Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$Rewarded;
public fun equals (Ljava/lang/Object;)Z
public final fun getAd ()Lcom/google/android/gms/ads/rewarded/RewardedAd;
public fun getLoadTime ()J
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class dev/teogor/ceres/monetisation/admob/formats/CacheAdModel$RewardedInterstitial : dev/teogor/ceres/monetisation/admob/formats/CacheAdModel {
public final class dev/teogor/ceres/monetisation/admob/formats/CachedAd$RewardedInterstitial : dev/teogor/ceres/monetisation/admob/formats/CachedAd {
public static final field $stable I
public fun <init> (Lcom/google/android/gms/ads/rewardedinterstitial/RewardedInterstitialAd;)V
public fun <init> (Lcom/google/android/gms/ads/rewardedinterstitial/RewardedInterstitialAd;J)V
public final fun component1 ()Lcom/google/android/gms/ads/rewardedinterstitial/RewardedInterstitialAd;
public final fun copy (Lcom/google/android/gms/ads/rewardedinterstitial/RewardedInterstitialAd;)Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$RewardedInterstitial;
public static synthetic fun copy$default (Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$RewardedInterstitial;Lcom/google/android/gms/ads/rewardedinterstitial/RewardedInterstitialAd;ILjava/lang/Object;)Ldev/teogor/ceres/monetisation/admob/formats/CacheAdModel$RewardedInterstitial;
public final fun component2 ()J
public final fun copy (Lcom/google/android/gms/ads/rewardedinterstitial/RewardedInterstitialAd;J)Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$RewardedInterstitial;
public static synthetic fun copy$default (Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$RewardedInterstitial;Lcom/google/android/gms/ads/rewardedinterstitial/RewardedInterstitialAd;JILjava/lang/Object;)Ldev/teogor/ceres/monetisation/admob/formats/CachedAd$RewardedInterstitial;
public fun equals (Ljava/lang/Object;)Z
public final fun getAd ()Lcom/google/android/gms/ads/rewardedinterstitial/RewardedInterstitialAd;
public fun getLoadTime ()J
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ abstract class Ad(

open fun show() = Unit

open fun reloadExpiredAd() {
val adTypeName = type().toFriendlyName(suffix = " Ad")
log("Loading a new $adTypeName to replace the expired ad.")
AdCache.removeAd(id)

load()
}

fun onListener(event: AdEvent) {
log("onListener::$event")
when (event) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,39 @@
package dev.teogor.ceres.monetisation.admob.formats

object AdCache {
private val adCache: MutableMap<String, MutableList<CacheAdModel>> = mutableMapOf()
private val cachedAds: MutableMap<String, MutableList<CachedAd>> = mutableMapOf()

@Synchronized
fun cacheAd(adId: String, ad: CacheAdModel) {
adCache.getOrPut(adId) {
fun cacheAd(adId: String, ad: CachedAd) {
cachedAds.getOrPut(adId) {
mutableListOf()
}.apply {
add(ad)
}
}

@Synchronized
fun getAd(adId: String): CacheAdModel? {
return adCache[adId]?.firstOrNull()
fun getAd(adId: String): CachedAd? {
return cachedAds[adId]?.firstOrNull()
}

@Synchronized
fun getAds(adId: String, count: Int): List<CacheAdModel> {
return adCache[adId]?.take(count) ?: emptyList()
fun getAds(adId: String, count: Int): List<CachedAd> {
return cachedAds[adId]?.take(count) ?: emptyList()
}

@Synchronized
fun removeAd(adId: String) {
adCache[adId]?.let { adList ->
cachedAds[adId]?.let { adList ->
if (adList.isNotEmpty()) {
adList.removeAt(0)
}
if (adList.isEmpty()) {
adCache.remove(adId)
cachedAds.remove(adId)
}
}
}

@Synchronized
fun getAdCount(adId: String) = adCache[adId]?.size ?: 0
fun getAdCount(adId: String) = cachedAds[adId]?.size ?: 0
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,29 @@ enum class AdType {

fun type(): AdType = this
}

/**
* Converts an AdType to a user-friendly name with an optional prefix and suffix.
*
* @param prefix The prefix to add to the friendly name.
* @param suffix The suffix to add to the friendly name.
* @return The user-friendly name of the AdType.
*/
fun AdType.toFriendlyName(
prefix: String = "",
suffix: String = "",
): String {
return this.name
.split(Regex("(?=[A-Z])"))
.joinToString(" ") {
it.replaceFirstChar { char ->
if (char.isLowerCase()) {
char.titlecase()
} else {
char.toString()
}
}
}
.trim()
.let { "$prefix$it$suffix" }
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,14 @@ import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.appopen.AppOpenAd
import dev.teogor.ceres.monetisation.admob.CurrentActivityHolder
import java.util.Date
import java.util.concurrent.TimeUnit

abstract class AppOpenAd : Ad() {

final override fun type() = AdType.AppOpen

override fun useCache() = false

private var loadTime: Long = 0

override fun load(): Boolean {
if (!super.load()) {
return false
Expand All @@ -54,10 +53,9 @@ abstract class AppOpenAd : Ad() {
log("onAdLoaded.")
AdCache.cacheAd(
adId = id,
ad = CacheAdModel.AppOpen(ad),
ad = CachedAd.AppOpen(ad, Date().time),
)
onListener(AdEvent.AdLoaded)
loadTime = Date().time
}

/**
Expand Down Expand Up @@ -94,15 +92,13 @@ abstract class AppOpenAd : Ad() {
return
}

if (!wasLoadTimeLessThanNHoursAgo(4)) {
log("Loading the app open ad because the previously loaded ad has expired.")
load()
val appOpenAd = (ad as CachedAd.AppOpen).ad

if (!wasLoadTimeLessThanNHoursAgo(ad.loadTime, 4)) {
reloadExpiredAd()
return
} else {
log("App open ad is still valid; no need to reload.")
}

val appOpenAd = (ad as CacheAdModel.AppOpen).ad
if (isShowing) {
log("The app open ad is already showing.")
return
Expand Down Expand Up @@ -152,9 +148,12 @@ abstract class AppOpenAd : Ad() {
CurrentActivityHolder.activity?.let { appOpenAd.show(it) }
}

private fun wasLoadTimeLessThanNHoursAgo(numHours: Long): Boolean {
val dateDifference: Long = Date().time - loadTime
val numMilliSecondsPerHour: Long = 3600000
return dateDifference < numMilliSecondsPerHour * numHours
private fun wasLoadTimeLessThanNHoursAgo(
adLoadTime: Long,
hoursThreshold: Long,
): Boolean {
val dateDifference: Long = Date().time - adLoadTime
val numMillisecondsPerHour: Long = TimeUnit.HOURS.toMillis(1)
return dateDifference < numMillisecondsPerHour * hoursThreshold
}
}

This file was deleted.

Loading