Skip to content

Commit e63b7fa

Browse files
committed
feat: Fix App Open ad race conditions and waterfall loading
This release addresses several critical race conditions in `AppOpenManager` related to concurrent ad loads, splash screen navigation, and resume behavior. It ensures that duplicate ad requests are deduplicated and that lifecycle callbacks are fired reliably. ### App Open Ad Improvements - **Duplicate Load Prevention**: Implemented a `pendingFetchCallback` mechanism in `fetchViaWaterfall` and `fetchAd`. If a load is already in progress (e.g., a background preload), subsequent calls attach to the existing fetch instead of starting a new one. - **Splash Auto-Navigation Fix**: Resolved an issue where orphaned timeout runnables would trigger `onFailedToLoad` after an ad had already successfully shown, causing the splash screen to navigate away prematurely. - **Resume Show Logic**: Updated `showAdIfAvailable()` to only skip when a dialog-based fetch is active. Background auto-reload preloads no longer block ads from showing when the app is resumed. - **Strategy Refinements**: - Added `hasBeenBackgrounded` flag to distinguish cold starts from app resumes. - Prevented `ON_DEMAND` strategy from triggering automatic loads during `onStart` on cold starts, allowing explicit `fetchAd` or `forceShow` calls to take priority. ### Logging & Debugging - **Enhanced Traceability**: Added comprehensive `AdDebugUtils.logEvent` calls across all loading paths, including `loading`, `showCachedAd`, `onAdLoaded`, and `onFailedToLoad` for both waterfall and standard AdMob paths. ### Sample App & Versioning - **Sample Updates**: Modified `MyApplication` to use `HYBRID` loading for app open ads by default and adjusted provider chain configurations. Updated `SplashActivity` UMP request parameters. - **Version Bump**: Incremented library version to `3.4.3` across all modules: - `ad-manage-kit` - `ad-manage-kit-billing` - `ad-manage-kit-core` - `ad-manage-kit-compose` - `ad-manage-kit-yandex` - **Documentation**: Updated `README.md`, `CHANGELOG.md`, and created detailed release notes for `v3.4.3`.
1 parent 65b1f73 commit e63b7fa

11 files changed

Lines changed: 284 additions & 35 deletions

File tree

AdManageKit/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ afterEvaluate {
6868
from(components["release"])
6969
groupId = "com.github.i2hammad"
7070
artifactId = "ad-manage-kit"
71-
version = "3.4.2"
71+
version = "3.4.3"
7272
}
7373

7474

AdManageKit/src/main/java/com/i2hammad/admanagekit/admob/AppOpenManager.kt

Lines changed: 151 additions & 2 deletions
Large diffs are not rendered by default.

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,22 @@ All notable changes to AdManageKit will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [3.4.3] - 2026-03-06
9+
10+
### Fixed
11+
- **Duplicate Ad Loads**: `fetchViaWaterfall(callback, timeout)` and `fetchAd(callback, timeout)` now check `isLoading` before starting, attaching to the in-progress fetch instead of creating a duplicate (both waterfall and non-waterfall paths)
12+
- **Splash Auto-Navigation**: Orphaned `pendingFetchTimeoutRunnable` no longer fires stale `onFailedToLoad` callbacks after the ad has already loaded and shown
13+
- **Resume Ad Show Blocked**: `showAdIfAvailable()` no longer skips when a background auto-reload preload is in progress (only skips for dialog-based fetches)
14+
- **ON_DEMAND Cold Start**: `onStart` no longer triggers automatic ad load for ON_DEMAND strategy on cold start; explicit `fetchAd`/`forceShow` handles it
15+
- **Hot-Start Duplicate Loads**: Fixed concurrent loads when returning to SplashActivity via hot start
16+
17+
### Added
18+
- **Background Fetch Callback Attachment**: `pendingFetchCallback` mechanism allows splash `fetchAd` to receive results from an already-running background preload
19+
- **Cold Start Detection**: `hasBeenBackgrounded` flag distinguishes cold start from resume for ON_DEMAND strategy
20+
- **Load Start Logging**: `AdDebugUtils.logEvent` `loading` event added to all ad load start paths
21+
- **Cached Ad Logging**: `AdDebugUtils.logEvent` `showCachedAd` event when cached ads are shown
22+
- **Comprehensive Logging**: `AdDebugUtils.logEvent` added to all previously missing load/failure paths in `AppOpenManager`
23+
824
## [3.4.2] - 2026-03-06
925

1026
### Fixed

README.md

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
AdManageKit is a comprehensive Android library designed to simplify the integration and management of Google AdMob ads, Google Play Billing, and User Messaging Platform (UMP) consent.
77

8-
**Latest Version `3.4.2`** improves App Open ad reliability with `autoRetryFailedAds` support and late-loading ad preservation.
8+
**Latest Version `3.4.3`** fixes race conditions in App Open ad waterfall loading — duplicate loads, splash auto-navigation, and resume ad show blocking.
99

1010
---
1111

@@ -54,27 +54,23 @@ Your callback implementations work on both branches without changes.
5454

5555
| Use Case | Recommended |
5656
|----------|-------------|
57-
| Production apps (stable) | **Main branch** (v3.4.2) |
57+
| Production apps (stable) | **Main branch** (v3.4.3) |
5858
| New projects wanting latest features | **Nextgen branch** (v4.1.1) |
5959
| Testing preloader system | **Nextgen branch** |
6060
| Risk-averse production | **Main branch** |
6161

6262
---
6363

64-
## What's New in 3.4.2
64+
## What's New in 3.4.3
6565

66-
### App Open Ads: `autoRetryFailedAds` Support
67-
App open ads now respect `AdManageKitConfig.autoRetryFailedAds`, matching interstitial, rewarded, and native ads. Failed loads are automatically retried via `AdRetryManager` with exponential backoff.
66+
### Duplicate Waterfall Load Prevention
67+
Concurrent waterfall loads from `onStart` preload and splash `fetchAd` are now deduplicated. The splash callback attaches to the in-progress fetch instead of starting a second one.
6868

69-
```kotlin
70-
AdManageKitConfig.apply {
71-
autoRetryFailedAds = true // Now respected by app open ads
72-
maxRetryAttempts = 3 // Used instead of hardcoded value
73-
}
74-
```
69+
### Splash Auto-Navigation Fix
70+
Orphaned timeouts no longer fire stale `onFailedToLoad` callbacks after the ad has already loaded — preventing the splash from navigating away while the ad is still displayed.
7571

76-
### Late-Loading Ad Preservation
77-
App open ads that load after the timeout has fired are now cached for later use instead of being discarded. The next `showAdIfAvailable()` call will use the cached ad instantly.
72+
### Resume Ad Show After Auto-Reload
73+
`showAdIfAvailable()` no longer skips when a background auto-reload preload is running. App open ads now show correctly on resume after auto-reload.
7874

7975
For previous versions, see the [Changelog](CHANGELOG.md) or individual [release notes](docs/release-notes/).
8076

@@ -117,15 +113,15 @@ dependencyResolutionManagement {
117113
<td>
118114

119115
```groovy
120-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v3.4.2'
121-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v3.4.2'
122-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v3.4.2'
116+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v3.4.3'
117+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v3.4.3'
118+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v3.4.3'
123119
124120
// For Jetpack Compose support
125-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v3.4.2'
121+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v3.4.3'
126122
127123
// For Yandex Ads multi-provider support
128-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v3.4.2'
124+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v3.4.3'
129125
```
130126

131127
</td>
@@ -253,7 +249,7 @@ Add Yandex (or other providers) as fallback ad networks with zero changes to you
253249

254250
```groovy
255251
// Add Yandex module
256-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v3.4.2'
252+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v3.4.3'
257253
```
258254

259255
```kotlin
@@ -603,6 +599,7 @@ AppPurchase.getInstance().changeSubscription(
603599
- [Multi-Provider Waterfall](docs/MULTI_PROVIDER_WATERFALL.md)
604600
- [Yandex Integration](docs/YANDEX_INTEGRATION.md)
605601
- [Billing Integration Guide](docs/APP_PURCHASE_GUIDE.md)
602+
- [Release Notes v3.4.3](docs/release-notes/RELEASE_NOTES_v3.4.3.md)
606603
- [Release Notes v3.4.2](docs/release-notes/RELEASE_NOTES_v3.4.2.md)
607604
- [Release Notes v3.4.1](docs/release-notes/RELEASE_NOTES_v3.4.1.md)
608605
- [Release Notes v3.4.0](docs/release-notes/RELEASE_NOTES_v3.4.0.md)

admanagekit-billing/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ afterEvaluate {
5151
from(components["release"]) // Now works correctly
5252
groupId = "com.github.i2hammad"
5353
artifactId = "ad-manage-kit-billing"
54-
version = "3.4.2"
54+
version = "3.4.3"
5555
}
5656
}
5757
}

admanagekit-compose/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ afterEvaluate {
8484
from(components["release"])
8585
groupId = "com.github.i2hammad"
8686
artifactId = "ad-manage-kit-compose"
87-
version = "3.4.2"
87+
version = "3.4.3"
8888
}
8989
}
9090
}

admanagekit-core/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ afterEvaluate {
4444
from(components["release"]) // Now works correctly
4545
groupId = "com.github.i2hammad"
4646
artifactId = "ad-manage-kit-core"
47-
version = "3.4.2"
47+
version = "3.4.3"
4848
}
4949
}
5050
}

admanagekit-yandex/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ afterEvaluate {
4444
from(components["release"])
4545
groupId = "com.github.i2hammad"
4646
artifactId = "ad-manage-kit-yandex"
47-
version = "3.4.2"
47+
version = "3.4.3"
4848
}
4949
}
5050
}

app/src/main/java/com/i2hammad/admanagekit/sample/MyApplication.kt

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.i2hammad.admanagekit.sample
22

33
import android.app.Application
4+
import androidx.lifecycle.ViewModelProvider.NewInstanceFactory.Companion.instance
45
import com.google.android.gms.ads.MobileAds
56
import com.google.android.gms.ads.RequestConfiguration
67
import com.i2hammad.admanagekit.admob.AppOpenManager
@@ -63,8 +64,8 @@ class MyApplication : Application() {
6364
maxCachedAdsPerUnit = 3
6465
maxCacheMemoryMB = 50
6566
enableWelcomeBackDialog = true
66-
appOpenFetchFreshAd = true
67-
appOpenAutoReload = false
67+
appOpenFetchFreshAd = false
68+
appOpenAutoReload = true
6869
welcomeDialogAppIcon = R.mipmap.ic_launcher_round
6970

7071
// =================== RELIABILITY FEATURES ===================
@@ -96,7 +97,7 @@ class MyApplication : Application() {
9697
// - Fetches fresh ad with loading dialog if cache is empty
9798
// - Good balance between UX and ad coverage
9899
interstitialLoadingStrategy = AdLoadingStrategy.HYBRID
99-
appOpenLoadingStrategy = AdLoadingStrategy.ON_DEMAND
100+
appOpenLoadingStrategy = AdLoadingStrategy.HYBRID
100101
nativeLoadingStrategy = AdLoadingStrategy.HYBRID
101102

102103
// Option 2: ONLY_CACHE - Best for smooth UX (gaming apps)
@@ -187,11 +188,17 @@ class MyApplication : Application() {
187188
))
188189

189190
// Configure provider chains: Yandex first, AdMob fallback
190-
AdProviderConfig.setInterstitialChain(listOf(yandex.interstitialProvider, admob.interstitialProvider))
191-
AdProviderConfig.setBannerChain(listOf(yandex.bannerProvider, admob.bannerProvider))
192-
AdProviderConfig.setNativeChain(listOf(yandex.nativeProvider, admob.nativeProvider))
193-
AdProviderConfig.setAppOpenChain(listOf(yandex.appOpenProvider, admob.appOpenProvider))
194-
AdProviderConfig.setRewardedChain(listOf(yandex.rewardedProvider, admob.rewardedProvider))
191+
AdProviderConfig.setInterstitialChain(listOf(admob.interstitialProvider, yandex.interstitialProvider))
192+
AdProviderConfig.setBannerChain(listOf(admob.bannerProvider, yandex.bannerProvider))
193+
AdProviderConfig.setNativeChain(listOf(admob.nativeProvider, yandex.nativeProvider))
194+
// AdProviderConfig.setAppOpenChain(listOf(admob.appOpenProvider, yandex.appOpenProvider))
195+
AdProviderConfig.setRewardedChain(listOf(admob.rewardedProvider, yandex.rewardedProvider))
196+
// // Configure provider chains: Yandex first, AdMob fallback
197+
// AdProviderConfig.setInterstitialChain(listOf(yandex.interstitialProvider, admob.interstitialProvider))
198+
// AdProviderConfig.setBannerChain(listOf(yandex.bannerProvider, admob.bannerProvider))
199+
// AdProviderConfig.setNativeChain(listOf(yandex.nativeProvider, admob.nativeProvider))
200+
// AdProviderConfig.setAppOpenChain(listOf(yandex.appOpenProvider, admob.appOpenProvider))
201+
// AdProviderConfig.setRewardedChain(listOf(yandex.rewardedProvider, admob.rewardedProvider))
195202
}
196203

197204
private fun initBilling() {

app/src/main/java/com/i2hammad/admanagekit/sample/SplashActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class SplashActivity : AppCompatActivity() {
7575
statusTextView.text = "Consent Requested."
7676

7777
adsConsentManager.requestUMP(
78-
this@SplashActivity, true, "EC60C39375F6619F5C03850A0E440646", true
78+
this@SplashActivity, false, "EC60C39375F6619F5C03850A0E440646", false
7979
) { isUserAccepted ->
8080
if (isUserAccepted) {
8181
Log.d("SplashActivity", "requestUMP: Consent accepted by user")

0 commit comments

Comments
 (0)