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 3.4.4 fixes native ad show rate issues across all native views and adds null safety to RewardedAdManager.
Beta Notice: The Next-Gen GMA SDK (
com.google.android.libraries.ads.mobile.sdk) is currently in beta by Google, and AdManageKit's nextgen branch is also in beta. The underlying Google SDK may receive breaking changes until it reaches stable release. For production use, the main branch is the stable option.
AdManageKit offers a Next-Gen GMA SDK version on the nextgen branch, featuring Google's modern preloader-based ad loading system.
| Feature | Main Branch (GMS SDK) | Next-Gen Branch |
|---|---|---|
| SDK | play-services-ads (stable) |
ads-mobile-sdk (beta) |
| Ad Loading | Traditional load/show | Preloader-based with auto-refill |
| Threading | Manual main thread dispatch | Automatic background thread safety |
| Buffer System | N/A | Configurable ad buffers per type |
| Background Handling | Basic | Smart pending ad queue |
- Preloader System: SDK automatically loads next ad after one is consumed
- Background-Aware Ads: App open ads won't show when app is in background
- Pending Ad Queue: Ads that load while backgrounded are saved for return
- Configurable Buffers: Set how many ads to keep ready per type
// Next-Gen preloader configuration
AdManageKitConfig.apply {
enableInterstitialPreloader = true
enableAppOpenPreloader = true
interstitialPreloaderBufferSize = 2
}Both branches use the same callback signatures via type aliases:
AdKitError→ resolves to appropriate SDK error typeAdKitLoadError→ resolves to appropriate SDK load error typeAdKitValue→ resolves to appropriate SDK value type
Your callback implementations work on both branches without changes.
| Use Case | Recommended |
|---|---|
| Production apps (stable) | Main branch (v3.4.4) |
| New projects wanting latest features | Nextgen branch (v4.1.1) |
| Testing preloader system | Nextgen branch |
| Risk-averse production | Main branch |
Fixed low show rates across all native ad views (NativeBannerSmall, NativeBannerMedium, NativeLarge, NativeTemplateView). Parent container visibility was not restored after a previous load failure, causing ads to load but remain invisible.
setNativeAd() was deferred via post {} which risked never executing. Changed to synchronous call for reliable impression registration. Also fixed shimmer overlay on cached/waterfall ads.
RewardedAdManager no longer crashes when methods are called before initialize(). All 5 public entry points now validate the ad unit ID and return appropriate callbacks.
For previous versions, see the Changelog or individual release notes.
| NativeBannerSmall Ad | Interstitial Ad | App Open Ad | UMP Consent Form |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
Watch a short demo of AdManageKit in action:
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:
| Main Branch (Stable GMS SDK) | Next-Gen Branch (Beta GMA SDK) |
|---|---|
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v3.4.4'
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v3.4.4'
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v3.4.4'
// For Jetpack Compose support
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v3.4.4'
// For Yandex Ads multi-provider support
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v3.4.4' |
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-nextgen:v4.1.1'
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing-nextgen:v4.1.1'
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core-nextgen:v4.1.1'
// For Jetpack Compose support
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose-nextgen:v4.1.1' |
Step 3: Sync your project with Gradle.
- 27 Template Styles: card_modern, material3, app_store, social_feed, gradient_card, pill_banner, medium_horizontal, flexible, icon_left, top_icon_media, spotlight, and more
- XML & Programmatic: Set templates via
app:adTemplateorsetTemplate() - Material 3 Theming: Automatic dark/light mode support
- AdChoices Control: Configure placement position (v2.9.0+)
- Video-Ready: All templates support video ads (120dp+ MediaView)
- View Documentation
- ON_DEMAND: Fetch fresh ads with loading dialog
- ONLY_CACHE: Instant display from cache
- HYBRID: Cache-first with fallback fetch (recommended)
- View Documentation
- BannerAdCompose, NativeAdCompose, InterstitialAdCompose
- Programmatic native ads without predefined layouts
- ConditionalAd, CacheWarmingEffect utilities
- Banner Ads: Auto-refresh, collapsible banners, smart retry
- Native Ads: Small, Medium, Large formats with caching
- Interstitial Ads: Time/count-based triggers, dialog support
- App Open Ads: Lifecycle-aware with activity exclusion
- AdManageKitConfig: Single configuration point
- Environment-specific settings (debug vs production)
- Runtime configuration changes
- Screen-aware caching prevents collisions
- Smart preloading with usage patterns
- LRU cache with configurable expiration
- 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
- Multiple Ad Networks: Load ads from AdMob, Yandex, and more with automatic fallback
- Zero Code Changes: Configure provider chains once; all existing API calls use waterfall automatically
- Per-Ad-Type Chains: Configure different provider orders for each ad format
- Region-Based: Prioritize providers by user locale (e.g., Yandex first for Russia)
- View Waterfall Documentation
- Yandex Integration Guide
- Core Module: Shared interfaces and configuration
- Compose Module: Jetpack Compose integration
- Billing Module: Google Play Billing Library v8
- Yandex Module: Yandex Ads SDK provider
Configure AdManageKit in your Application class:
class MyApp : Application() {
private lateinit var appOpenManager: AppOpenManager
override fun onCreate() {
super.onCreate()
// Configure AdManageKit
AdManageKitConfig.apply {
debugMode = BuildConfig.DEBUG
enableSmartPreloading = true
autoRetryFailedAds = true
// Ad Loading Strategies (v2.6.0+)
interstitialLoadingStrategy = AdLoadingStrategy.HYBRID
appOpenLoadingStrategy = AdLoadingStrategy.HYBRID
nativeLoadingStrategy = AdLoadingStrategy.HYBRID
// Auto-reload ads after showing (v2.7.0+)
interstitialAutoReload = true // default: true
appOpenAutoReload = true // default: true
rewardedAutoReload = true // default: true
}
// Set up billing
BillingConfig.setPurchaseProvider(BillingPurchaseProvider())
// Initialize app open ads
appOpenManager = AppOpenManager(this, "your-app-open-ad-unit-id")
}
}Add Yandex (or other providers) as fallback ad networks with zero changes to your existing ad loading code:
// Add Yandex module
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v3.4.4'// In Application.onCreate(), after AdManageKitConfig setup:
YandexProviderRegistration.initialize(this)
val admob = AdMobProviderRegistration.create()
val yandex = YandexProviderRegistration.create()
// Map your AdMob ad unit IDs to Yandex equivalents
AdUnitMapping.register("ca-app-pub-xxx/your-interstitial", mapOf("yandex" to "R-M-XXXXXX-Y"))
AdUnitMapping.register("ca-app-pub-xxx/your-native", mapOf("yandex" to "R-M-XXXXXX-Y"))
// Set provider chains (order = priority)
AdProviderConfig.setInterstitialChain(listOf(admob.interstitialProvider, yandex.interstitialProvider))
AdProviderConfig.setNativeChain(listOf(admob.nativeProvider, yandex.nativeProvider))
// ... same for banner, app open, rewardedSee Multi-Provider Waterfall and Yandex Integration for the full guide.
<com.i2hammad.admanagekit.admob.NativeTemplateView
android:id="@+id/nativeTemplateView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:adTemplate="material3" />| Template | Best For |
|---|---|
card_modern |
General use |
material3 |
M3 apps |
minimal |
Content-focused |
compact_horizontal |
Lists |
list_item |
RecyclerView items |
magazine |
News/blog apps |
app_store |
App promotion (v2.9.0+) |
social_feed |
Feed integration (v2.9.0+) |
gradient_card |
Premium feel (v2.9.0+) |
pill_banner |
Inline placement (v2.9.0+) |
medium_horizontal |
55/45 media-content split (v3.0.0+) |
spotlight |
High visibility (v2.9.0+) |
media_content_split |
Balanced display (v2.9.0+) |
flexible |
Adaptive layout (v3.3.2+) |
icon_left |
Icon on left, GridView optimized (v3.3.2+) |
top_icon_media |
Icon at top, MediaView center (v3.3.2+) |
video_small/medium/large |
Video content |
video_square/vertical/fullscreen |
Social feeds |
// Load with default template
nativeTemplateView.loadNativeAd(activity, "ca-app-pub-xxx/yyy")
// Change template
nativeTemplateView.setTemplate(NativeAdTemplate.MAGAZINE)
nativeTemplateView.loadNativeAd(activity, "ca-app-pub-xxx/yyy")
// With callback
nativeTemplateView.loadNativeAd(activity, adUnitId, object : AdLoadCallback() {
override fun onAdLoaded() { /* success */ }
override fun onFailedToLoad(error: AdError?) { /* error */ }
})
// With strategy override
nativeTemplateView.loadNativeAd(activity, adUnitId, callback, AdLoadingStrategy.ONLY_CACHE)<com.i2hammad.admanagekit.admob.BannerAdView
android:id="@+id/bannerAdView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />bannerAdView.loadBanner(this, "ca-app-pub-xxx/yyy")
// Collapsible banner
bannerAdView.loadCollapsibleBanner(this, "ca-app-pub-xxx/yyy", true)<com.i2hammad.admanagekit.admob.NativeBannerSmall
android:id="@+id/nativeBannerSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content" />nativeBannerSmall.loadNativeBannerAd(this, "ca-app-pub-xxx/yyy")
// With caching
nativeBannerSmall.loadNativeBannerAd(activity, adUnitId, useCachedAd = true)// Load
AdManager.getInstance().loadInterstitialAd(this, "ca-app-pub-xxx/yyy")
// Show immediately
AdManager.getInstance().forceShowInterstitial(this, object : AdManagerCallback() {
override fun onNextAction() { navigateNext() }
})
// Show with dialog
AdManager.getInstance().forceShowInterstitialWithDialog(this, callback)
// Time-based (every 15 seconds)
AdManager.getInstance().showInterstitialAdByTime(this, callback)
// Count-based
AdManager.getInstance().showInterstitialAdByCount(this, callback, maxDisplayCount = 3)Control whether interstitial ads automatically reload after being shown:
// Global config (applies to all AdManager methods)
AdManageKitConfig.interstitialAutoReload = false // Disable auto-reload
// Per-call override via InterstitialAdBuilder
InterstitialAdBuilder.with(activity)
.adUnit(adUnitId)
.autoReload(true) // Override global setting for this call
.show { navigateNext() }
// Per-call override via AdManager
AdManager.getInstance().showInterstitialIfReady(activity, callback, reloadAd = false)Priority: InterstitialAdBuilder.autoReload() > AdManageKitConfig.interstitialAutoReload
// Initialize once (e.g., in Application.onCreate())
RewardedAdManager.initialize(context, "ca-app-pub-xxx/yyy")
// Show when ready
if (RewardedAdManager.isAdLoaded()) {
RewardedAdManager.showAd(activity, object : RewardedAdManager.RewardedAdCallback {
override fun onRewardEarned(rewardType: String, rewardAmount: Int) {
grantReward(rewardType, rewardAmount)
}
override fun onAdDismissed() {
continueGameFlow()
}
})
}
// Load with timeout (splash screens)
RewardedAdManager.loadRewardedAdWithTimeout(context, 5000, callback)
// Preload during natural pauses
RewardedAdManager.preload(context)
// Control auto-reload
AdManageKitConfig.rewardedAutoReload = false // Disable globally
RewardedAdManager.showAd(activity, callback, autoReload = false) // Per-call override// In Application class
appOpenManager = AppOpenManager(this, "ca-app-pub-xxx/yyy")
// Exclude activities
appOpenManager.disableAppOpenWithActivity(MainActivity::class.java)
// Force show
appOpenManager.forceShowAdIfAvailable(activity, callback)
// Skip next ad
appOpenManager.skipNextAd()For apps with one activity and multiple fragments:
// Set current screen when navigating
navController.addOnDestinationChangedListener { _, destination, _ ->
appOpenManager.setCurrentScreenTag(destination.label?.toString())
}
// Exclude specific screens
appOpenManager.excludeScreenTags("Payment", "Onboarding", "Checkout")
// Or use fragment tag provider
appOpenManager.setFragmentTagProvider {
supportFragmentManager.fragments.lastOrNull()?.tag
}
appOpenManager.excludeFragmentTags("PaymentFragment", "OnboardingFragment")
// Temporarily disable during critical flows
appOpenManager.disableAppOpenAdsTemporarily()
// ... perform operation ...
appOpenManager.enableAppOpenAds()@Composable
fun MyScreen() {
// Banner
BannerAdCompose(adUnitId = "ca-app-pub-xxx/yyy")
// NativeTemplateView with any template (v2.6.0+)
NativeTemplateCompose(
adUnitId = "ca-app-pub-xxx/yyy",
template = NativeAdTemplate.MATERIAL3,
loadingStrategy = AdLoadingStrategy.HYBRID
)
// Native with loading strategy (ON_DEMAND or HYBRID only)
NativeBannerMediumCompose(
adUnitId = "ca-app-pub-xxx/yyy",
loadingStrategy = AdLoadingStrategy.HYBRID
)
// Interstitial
val showInterstitial = rememberInterstitialAd(
adUnitId = "ca-app-pub-xxx/yyy",
preloadAd = true
)
Button(onClick = { showInterstitial() }) {
Text("Show Ad")
}
// Conditional (hides for purchased users)
ConditionalAd {
ProgrammaticNativeBannerMediumCompose(adUnitId = "ca-app-pub-xxx/yyy")
}
}AdsConsentManager.getInstance(this).requestUMP(
activity = this,
isDebug = true,
testDeviceId = "TEST_DEVICE_ID",
resetConsent = false,
listener = object : UMPResultListener {
override fun onCheckUMPSuccess(isConsentGiven: Boolean) {
if (isConsentGiven) {
// Initialize and load ads here
AdManager.getInstance().loadInterstitialAd(activity, adUnitId)
}
}
}
)// Define products with categories
val products = listOf(
PurchaseItem("coins_100", TYPE_IAP.PURCHASE, PurchaseCategory.CONSUMABLE),
PurchaseItem("remove_ads", TYPE_IAP.PURCHASE, PurchaseCategory.REMOVE_ADS),
PurchaseItem("lifetime", TYPE_IAP.PURCHASE, PurchaseCategory.LIFETIME_PREMIUM),
PurchaseItem("premium_monthly", "free_trial", TYPE_IAP.SUBSCRIPTION)
)
// Initialize
AppPurchase.getInstance().initBilling(application, products)
// Purchase
AppPurchase.getInstance().purchase(activity, "remove_ads")
// Subscribe
AppPurchase.getInstance().subscribe(activity, "premium_monthly")
// Check status
if (AppPurchase.getInstance().isPurchased()) {
// User has premium (subscription, lifetime, or remove_ads)
}
// Track purchases and handle consumables
AppPurchase.getInstance().setPurchaseHistoryListener(object : PurchaseHistoryListener {
override fun onNewPurchase(productId: String, purchase: PurchaseResult) {
if (productId == "coins_100") {
addCoins(100 * purchase.quantity)
AppPurchase.getInstance().consumePurchase(productId) // Manual consume
}
}
override fun onPurchaseConsumed(productId: String, purchase: PurchaseResult) { }
})// Check subscription state
val state = AppPurchase.getInstance().getSubscriptionState("premium_monthly")
when (state) {
SubscriptionState.ACTIVE -> showPremiumUI()
SubscriptionState.CANCELLED -> showRenewalPrompt() // Still has access
SubscriptionState.EXPIRED -> showSubscribeButton()
}
// Upgrade subscription
AppPurchase.getInstance().upgradeSubscription(activity, "premium_yearly")
// Downgrade subscription
AppPurchase.getInstance().downgradeSubscription(activity, "premium_basic")
// Full control with proration mode
AppPurchase.getInstance().changeSubscription(
activity,
"premium_monthly",
"premium_yearly",
SubscriptionReplacementMode.CHARGE_PRORATED_PRICE
)- NativeTemplateView Guide
- Ad Loading Strategies
- Jetpack Compose Integration
- Native Ads Caching
- Interstitial Ads
- Rewarded Ads
- App Open Ads
- Multi-Provider Waterfall
- Yandex Integration
- Billing Integration Guide
- Release Notes v3.4.4
- Release Notes v3.4.3
- Release Notes v3.4.2
- Release Notes v3.4.1
- Release Notes v3.4.0
- Release Notes v3.3.9
- Release Notes v3.3.8
- Release Notes v3.3.7
- Release Notes v3.3.6
- Release Notes v3.3.5
- Release Notes v3.3.4
- Release Notes v3.3.3
- Release Notes v3.3.2
- Release Notes v3.3.0
- Release Notes v3.1.0
- Release Notes v3.0.0
- API Reference
- Multi-Provider Waterfall
- Yandex Integration
- Rewarded Ads
- Billing Integration
- Purchase Categories
- Consumable Products
- Subscriptions
- Subscription Upgrades
Online: https://i2hammad.github.io/AdManageKit/
Generate locally:
./gradlew dokkaGenerateHtmlOutput: build/dokka/html/index.html
AdManageKit provides an MCP (Model Context Protocol) server that integrates with AI tools like Claude Code, Claude Desktop, Cursor, and other MCP-compatible clients. It provides 10 tools for documentation lookup and code generation.
| Category | Tools |
|---|---|
| Documentation | search_docs, get_doc_by_topic, get_api_reference, get_release_notes, get_migration_guide, list_documentation |
| Code Generation | generate_config, generate_ad_integration, generate_billing_code, generate_compose_code |
Claude Desktop / Cursor (claude_desktop_config.json):
{
"mcpServers": {
"admanagekit": {
"command": "npx",
"args": ["-y", "admanagekit-mcp-server"]
}
}
}Claude Code (auto-configured via .mcp.json when working in this repo):
{
"mcpServers": {
"admanagekit": {
"command": "node",
"args": ["mcp-server/dist/index.js"]
}
}
}- Search documentation across all docs, wiki pages, and API references
- Look up API references for any class (AdManager, AppOpenManager, NativeAdManager, etc.)
- Generate integration code for any ad type with display modes, loading strategies, and callbacks
- Generate billing code for purchases, subscriptions, consumables, and expiry verification
- Generate Compose code for all Compose ad components
- Supports both Kotlin and Java output
See mcp-server/ for more details.
Version 3.0.0 is fully backward compatible. Optionally adopt new features:
Replace separate load + show calls with single showOrWaitForAd():
// Before (v2.9.0) - Two-step approach
AdManager.getInstance().loadInterstitialAdForSplash(this, adUnitId, 10_000, object : AdManagerCallback() {
override fun onNextAction() {
AdManager.getInstance().forceShowInterstitial(this@SplashActivity, callback)
}
})
// After (v3.0.0) - Single smart call
AdManager.getInstance().showOrWaitForAd(
activity = this,
callback = object : AdManagerCallback() {
override fun onNextAction() { navigateNext() }
},
timeoutMillis = 10_000
)// Before (single ad unit)
AdManager.getInstance().loadInterstitialAd(context, "single_unit")
// After (multiple ad units for redundancy)
AdManager.getInstance().loadMultipleAdUnits(context, "high_ecpm", "medium_ecpm", "fallback")// Prefetch before external intents
appOpenManager.prefetchNextAd()
startActivityForResult(cameraIntent, REQUEST_CODE)Version 2.9.0 has one breaking change for consumable products:
Consumables are no longer auto-consumed. You must manually call consumePurchase():
// Before v2.9.0 (auto-consume)
AppPurchase.getInstance().setConsumePurchase(true) // Deprecated
// After v2.9.0 (manual consume)
AppPurchase.getInstance().setPurchaseHistoryListener(object : PurchaseHistoryListener {
override fun onNewPurchase(productId: String, purchase: PurchaseResult) {
grantItems(productId, purchase.quantity)
AppPurchase.getInstance().consumePurchase(productId) // Manual!
}
override fun onPurchaseConsumed(productId: String, purchase: PurchaseResult) { }
})Use Purchase Categories for better product classification:
// Before
PurchaseItem("coins", TYPE_IAP.PURCHASE, true) // isConsumable
// After (explicit categories)
PurchaseItem("coins", TYPE_IAP.PURCHASE, PurchaseCategory.CONSUMABLE)
PurchaseItem("remove_ads", TYPE_IAP.PURCHASE, PurchaseCategory.REMOVE_ADS)Version 2.8.0 is fully backward compatible with one behavioral change:
forceShowInterstitial() now respects loading strategy:
// If you need old behavior (always force fetch), use:
AdManager.getInstance().forceShowInterstitialAlways(activity, callback)
// Or set strategy to ON_DEMAND globally:
AdManageKitConfig.interstitialLoadingStrategy = AdLoadingStrategy.ON_DEMANDVersion 2.7.0 is fully backward compatible. Optionally adopt new features:
// Old way (still works)
val nativeBannerMedium = NativeBannerMedium(context)
nativeBannerMedium.loadNativeBannerAd(activity, adUnitId)
// New unified approach
val nativeTemplateView = NativeTemplateView(context)
nativeTemplateView.setTemplate(NativeAdTemplate.CARD_MODERN)
nativeTemplateView.loadNativeAd(activity, adUnitId)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
- Fork the repository
- Create a branch (
git checkout -b feature/YourFeature) - Commit changes (
git commit -m 'Add YourFeature') - Push (
git push origin feature/YourFeature) - Open a Pull Request
Licensed under the MIT License. See LICENSE.
For issues: GitHub Issues or hammadmughal0001@gmail.com



