-
Notifications
You must be signed in to change notification settings - Fork 4
Home
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.
Latest Version: 4.4.5
Patch release, no API changes.
-
A banner loaded successfully but rendered blank under Compose —
BannerAdViewswaps the shimmer placeholder for the loadedAdView, which raises an ordinaryrequestLayout(). That request cannot cross Compose'sAndroidViewinterop boundary once any ancestor already carries a pending layout flag, so Compose never re-measured the subtree and the freshly attachedAdViewwas left at 0×0. The ad loaded,onAdLoaded/onAdImpressionfired and the impression was logged and billed, but the slot stayed blank until a rotation or resize forced a full traversal. Both the AdMob andBannerWaterfallsuccess paths now force the measure/layout pass themselves; XML-hosted banners are unaffected - Dependencies — Next-Gen GMA SDK 1.4.0, Android Gradle Plugin 9.3.2, Firebase BOM 34.18.0
Critical billing hotfix, no API changes. If you ship the billing module on 4.4.3, upgrade.
-
Billing never connected on 4.4.3 —
connectToGooglePlayBilling()guarded onbillingClient.isReady(), which reportstruethe instant a client is built, sostartConnection(...)was skipped on every fresh client. Setup never ran, no product details or entitlement were fetched, and there was no failure callback and no error log to explain it. The guard is back onisServiceConnected; the re-query paths keep the 4.4.3isReady()fix -
A timed-out setup reported itself as initialized — the
setBillingListener(listener, timeout)timeout path setisBillingInitialized = truebefore deliveringSERVICE_TIMEOUT, so host-app guards of the formif (!initBillingFinish) initBilling()went permanently quiet and the connection could never be retried. It now setsfalse -
Billing connection lifecycle logging —
initBilling,connectToGooglePlayBillingand bothBillingClientStateListenercallbacks now log their state atDEBUGunder theAppPurchasetag, so this class of failure is visible inlogcat
Bug-fix and dependency release, no API changes. Two silent-failure bugs, both of which cost money.
⚠️ 4.4.3's billing regression makes purchases unusable — use 4.4.4 or newer.
- A timed-out rewarded load could sabotage the one that replaced it — a request handed to the SDK cannot be cancelled, and when it finally reported back it could discard an ad a newer load had just delivered, clear the loading flag out from under a request still in flight, and fail callers waiting on a load that had not finished. Every load path now carries a generation token, and a late ad is kept for the next show instead of dropped
-
Billing could stop acknowledging purchases for the rest of the process — the connection flag could latch
falseafter one disconnect while the client was actually ready, disabling every purchase re-query including the acknowledgment retry. Play auto-refunds an unacknowledged purchase after 3 days -
Acknowledgment no longer requires a configured product id — a
PURCHASEDorder the current build does not list (promo grants, dropped products, a Console typo) was never acknowledged, and so was auto-refunded on day 3 - Dependencies — Next-Gen GMA SDK 1.3.1, Yandex Mobile Ads 8.3.0, Compose BOM 2026.08.00, Firebase BOM 34.17.0, AppCompat 1.8.0
Pending purchases need one thing from your app: call
refreshPurchases()from your main activity'sonResume(). See Billing Integration.
-
4.4.2 — Rewarded callbacks marshalled to the main thread (they could crash the app), a completed purchase could fail to disable ads, blank gaps where banner/native slots should have collapsed, and a
BannerAdViewActivity leak. Behavior changes: an account-hold subscription no longer disables ads, and premium users no longer reserve ad space in Compose - 4.4.1 — Google Mobile Ads Next-Gen SDK 1.3.0 (from 1.2.1), plus repairs to API doc generation and the MCP documentation server
-
4.4.0 — Subscription offers can be purchased individually (
subscribe(activity, offer)), offer lookup by id/base plan/tag, cross-cadence price normalization (BillingPeriod,getSavingsPercent), trial eligibility, Play Billing 9 one-time product offers, and client-side account hold detection. See Subscription Offers -
4.3.x — All standard banner sizes (
BannerAdSize), custom native templates, and app-open ad freshness enforcement -
4.2.0 — Migrated to the Google Mobile Ads Next-Gen SDK and Play Billing 9.
MobileAds.initialize()must now be called explicitly before any ad request
Upgrading from 3.x or earlier? Read the Migrating to 4.2.0 notes first — it is the one release in the 4.x line that is not source-compatible.
Full details: Changelog · Release Notes
- Banner Ads: Auto-refresh, collapsible banners, smart retry
- Native Ads: Small, Medium, Large formats with caching
- Interstitial Ads: Time/count-based triggers, dialog support, loading strategies
- App Open Ads: Lifecycle-aware with activity exclusion
- 38 Template Styles: card_modern, material3, minimal, list_item, magazine, app_store, social_feed, spotlight, plus the video_* and flat_* families
- XML & Programmatic: Set templates via
app:adTemplateorsetTemplate() - Custom Templates (v4.3.0+): supply your own layout via
setCustomTemplate()orapp:customAdLayout - Material 3 Theming: Automatic dark/light mode support
- ON_DEMAND: Fetch fresh ads with loading dialog
- ONLY_CACHE: Instant display from cache
- HYBRID: Cache-first with fallback fetch (recommended)
- FRESH_WITH_CACHE_FALLBACK: Fetch fresh, fall back to cache on failure
- AdManageKitConfig: Single configuration point
- Environment-specific settings (debug vs production)
- Runtime configuration changes
- Smart retry with exponential backoff
- Circuit breaker for failing ad units
- Memory leak prevention with WeakReference
- UMP consent management (GDPR/CCPA)
- Automatic ad hiding for purchased users
Step 1: Add JitPack to your root build.gradle:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
}Step 2: Add dependencies to your app's build.gradle:
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v4.4.5'
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v4.4.5'
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.5'
// For Jetpack Compose support
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v4.4.5'
// For Yandex Ads multi-provider support
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v4.4.5'Step 3: Ensure your app's compileSdk is 37 or higher (required transitively as of 4.2.0).
Step 4: Sync your project with Gradle.
Configure AdManageKit in your Application class:
Since 4.2.0 you must call
MobileAds.initialize()yourself. The Next-Gen SDK removed the legacy SDK's silent lazy-init, so an app that skips it never loads ads. AdManageKit does not call it for you, because it does not own your consent flow.
class MyApp : Application() {
var appOpenManager: AppOpenManager? = null
override fun onCreate() {
super.onCreate()
initAds()
// Set up billing
BillingConfig.setPurchaseProvider(BillingPurchaseProvider())
// Configure AdManageKit
AdManageKitConfig.apply {
debugMode = BuildConfig.DEBUG
enableSmartPreloading = true
autoRetryFailedAds = true
// Ad Loading Strategies
interstitialLoadingStrategy = AdLoadingStrategy.HYBRID
appOpenLoadingStrategy = AdLoadingStrategy.HYBRID
nativeLoadingStrategy = AdLoadingStrategy.HYBRID
// Auto-reload interstitial after showing
interstitialAutoReload = true // default: true
}
}
private fun initAds() {
val config = InitializationConfig.Builder(readApplicationIdFromManifest()).build()
// initialize() blocks, so keep it off the main thread or it can ANR.
Thread {
MobileAds.initialize(this, config)
// Construct AppOpenManager only after initialize() returns. Constructing it
// arms its ProcessLifecycleOwner observer, and onStart() fires as soon as any
// activity starts — created earlier, that observer can race ahead of
// initialization and issue a load the Next-Gen SDK rejects as "not initialized".
Handler(Looper.getMainLooper()).post {
appOpenManager = AppOpenManager(this, "your-app-open-ad-unit-id")
}
}.start()
}
}The Next-Gen SDK no longer reads the application id from the manifest automatically, so readApplicationIdFromManifest() pulls com.google.android.gms.ads.APPLICATION_ID from your ApplicationInfo metadata. See the sample app's MyApplication.kt for the full version.
- Interstitial Ads - Complete guide to interstitial ad integration
- Rewarded Ads - Rewarded video ads with callbacks and analytics
- App Open Ads - App open ad implementation
- Native Ads - Native ad caching and NativeTemplateView
- Banner Ads - Banner ad integration
- Ad Loading Strategies - ON_DEMAND, ONLY_CACHE, HYBRID strategies
- Configuration - Complete AdManageKitConfig reference
- Jetpack Compose - Compose integration and helpers
- Multi-Provider Waterfall - Load ads from multiple networks with automatic fallback
- Yandex Integration - Yandex Ads SDK provider setup and configuration
- Billing Integration - Play Billing setup, products, and purchase flows
- Purchase Categories - Product type classification
- Consumable Products - Consumable in-app purchases
- Subscriptions - Subscription state, renewal, and account hold
- Subscription Offers - Multi-offer paywalls, price comparison, trial eligibility
- Subscription Upgrades - Plan changes and proration modes
The app module demonstrates all features. To run:
- Clone:
git clone https://github.com/i2hammad/AdManageKit.git - Open in Android Studio
- Replace placeholder AdMob IDs
- Run on device or emulator
For issues: GitHub Issues or hammadmughal0001@gmail.com
Licensed under the MIT License. See LICENSE.
AdManageKit v3.3.4 | GitHub | API Docs | Report Issue | Buy me a coffee