admanagekit-mcp-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/content/README.md +851 -0
- package/content/docs/AD_FREQUENCY_CONTROL.md +450 -0
- package/content/docs/AD_LOADING_STRATEGIES.md +369 -0
- package/content/docs/API_REFERENCE.md +734 -0
- package/content/docs/APP_PURCHASE_GUIDE.md +704 -0
- package/content/docs/BANNER_AD_IMPROVEMENTS.md +325 -0
- package/content/docs/COMPOSE_INTEGRATION.md +477 -0
- package/content/docs/CONFIGURATION_USAGE.md +168 -0
- package/content/docs/INTERSTITIAL_BUILDER_GUIDE.md +518 -0
- package/content/docs/JAVA_USAGE_GUIDE.md +785 -0
- package/content/docs/LOADING_STRATEGY_EXAMPLES.md +463 -0
- package/content/docs/NATIVE_AD_MANAGER_ENHANCEMENTS.md +347 -0
- package/content/docs/NATIVE_AD_PRELOADING.md +467 -0
- package/content/docs/NATIVE_TEMPLATE_VIEW.md +413 -0
- package/content/docs/app-open-ads.md +412 -0
- package/content/docs/interstitial-ads.md +269 -0
- package/content/docs/native-ads-caching.md +182 -0
- package/content/docs/release-notes/RELEASE_NOTES_v2.1.0.md +156 -0
- package/content/docs/release-notes/RELEASE_NOTES_v2.2.0.md +248 -0
- package/content/docs/release-notes/RELEASE_NOTES_v2.3.0.md +280 -0
- package/content/docs/release-notes/RELEASE_NOTES_v2.5.0.md +73 -0
- package/content/docs/release-notes/RELEASE_NOTES_v2.6.0.md +265 -0
- package/content/docs/release-notes/RELEASE_NOTES_v2.7.0.md +168 -0
- package/content/docs/release-notes/RELEASE_NOTES_v2.8.0.md +153 -0
- package/content/docs/release-notes/RELEASE_NOTES_v2.9.0.md +479 -0
- package/content/docs/release-notes/RELEASE_NOTES_v3.0.0.md +343 -0
- package/content/docs/release-notes/RELEASE_NOTES_v3.1.0.md +131 -0
- package/content/docs/release-notes/RELEASE_NOTES_v3.3.0.md +252 -0
- package/content/docs/release-notes/RELEASE_NOTES_v3.3.2.md +215 -0
- package/content/docs/release-notes/RELEASE_NOTES_v3.3.3.md +116 -0
- package/content/docs/release-notes/RELEASE_NOTES_v3.3.4.md +142 -0
- package/content/docs/release-notes/RELEASE_NOTES_v3.3.5.md +94 -0
- package/content/docs/rewarded-ads.md +623 -0
- package/content/wiki/Ad-Loading-Strategies.md +209 -0
- package/content/wiki/App-Open-Ads.md +189 -0
- package/content/wiki/Banner-Ads.md +133 -0
- package/content/wiki/Billing-Integration.md +69 -0
- package/content/wiki/Configuration.md +252 -0
- package/content/wiki/Consumable-Products.md +166 -0
- package/content/wiki/Home.md +144 -0
- package/content/wiki/Interstitial-Ads.md +313 -0
- package/content/wiki/Jetpack-Compose.md +270 -0
- package/content/wiki/NativeAdManager.md +220 -0
- package/content/wiki/Purchase-Categories.md +94 -0
- package/content/wiki/Rewarded-Ads.md +420 -0
- package/content/wiki/Subscription-Upgrades.md +278 -0
- package/content/wiki/Subscriptions.md +368 -0
- package/content/wiki/_Footer.md +3 -0
- package/content/wiki/_Sidebar.md +18 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +18 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/code-generation.d.ts +2 -0
- package/dist/tools/code-generation.js +264 -0
- package/dist/tools/code-generation.js.map +1 -0
- package/dist/tools/documentation.d.ts +2 -0
- package/dist/tools/documentation.js +202 -0
- package/dist/tools/documentation.js.map +1 -0
- package/dist/types.d.ts +18 -0
- package/dist/types.js +83 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/doc-loader.d.ts +17 -0
- package/dist/utils/doc-loader.js +245 -0
- package/dist/utils/doc-loader.js.map +1 -0
- package/dist/utils/search.d.ts +2 -0
- package/dist/utils/search.js +62 -0
- package/dist/utils/search.js.map +1 -0
- package/dist/utils/templates.d.ts +60 -0
- package/dist/utils/templates.js +793 -0
- package/dist/utils/templates.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,851 @@
|
|
|
1
|
+
# AdManageKit
|
|
2
|
+
[](https://jitpack.io/#i2hammad/AdManageKit)
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
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.
|
|
7
|
+
|
|
8
|
+
**Latest Version `3.3.5`** adds **App Open Loading Strategies** - proper loading strategy support (ON_DEMAND, ONLY_CACHE, HYBRID) with ad freshness tracking and auto-reload configuration.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Next-Gen GMA SDK Version
|
|
13
|
+
|
|
14
|
+
> **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.
|
|
15
|
+
|
|
16
|
+
AdManageKit offers a **Next-Gen GMA SDK** version on the `nextgen` branch, featuring Google's modern preloader-based ad loading system.
|
|
17
|
+
|
|
18
|
+
### Why Next-Gen?
|
|
19
|
+
|
|
20
|
+
| Feature | Main Branch (GMS SDK) | Next-Gen Branch |
|
|
21
|
+
|---------|----------------------|-----------------|
|
|
22
|
+
| SDK | `play-services-ads` (stable) | `ads-mobile-sdk` (beta) |
|
|
23
|
+
| Ad Loading | Traditional load/show | Preloader-based with auto-refill |
|
|
24
|
+
| Threading | Manual main thread dispatch | Automatic background thread safety |
|
|
25
|
+
| Buffer System | N/A | Configurable ad buffers per type |
|
|
26
|
+
| Background Handling | Basic | Smart pending ad queue |
|
|
27
|
+
|
|
28
|
+
### Next-Gen Features
|
|
29
|
+
|
|
30
|
+
- **Preloader System**: SDK automatically loads next ad after one is consumed
|
|
31
|
+
- **Background-Aware Ads**: App open ads won't show when app is in background
|
|
32
|
+
- **Pending Ad Queue**: Ads that load while backgrounded are saved for return
|
|
33
|
+
- **Configurable Buffers**: Set how many ads to keep ready per type
|
|
34
|
+
|
|
35
|
+
```kotlin
|
|
36
|
+
// Next-Gen preloader configuration
|
|
37
|
+
AdManageKitConfig.apply {
|
|
38
|
+
enableInterstitialPreloader = true
|
|
39
|
+
enableAppOpenPreloader = true
|
|
40
|
+
interstitialPreloaderBufferSize = 2
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Migration Compatibility
|
|
45
|
+
|
|
46
|
+
Both branches use the same callback signatures via type aliases:
|
|
47
|
+
- `AdKitError` → resolves to appropriate SDK error type
|
|
48
|
+
- `AdKitLoadError` → resolves to appropriate SDK load error type
|
|
49
|
+
- `AdKitValue` → resolves to appropriate SDK value type
|
|
50
|
+
|
|
51
|
+
Your callback implementations work on both branches without changes.
|
|
52
|
+
|
|
53
|
+
### Which Version Should I Use?
|
|
54
|
+
|
|
55
|
+
| Use Case | Recommended |
|
|
56
|
+
|----------|-------------|
|
|
57
|
+
| Production apps (stable) | **Main branch** (v3.3.5) |
|
|
58
|
+
| New projects wanting latest features | **Nextgen branch** (v4.1.1) |
|
|
59
|
+
| Testing preloader system | **Nextgen branch** |
|
|
60
|
+
| Risk-averse production | **Main branch** |
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## What's New in 3.3.5
|
|
65
|
+
|
|
66
|
+
### App Open Loading Strategies
|
|
67
|
+
- **Proper Loading Strategy Support**: AppOpenManager now fully supports `AdLoadingStrategy` (ON_DEMAND, ONLY_CACHE, HYBRID)
|
|
68
|
+
- **Ad Freshness Tracking**: Cached ads track load time to prevent showing stale ads
|
|
69
|
+
- **Smart Cache Usage**: ON_DEMAND strategy uses cached ads if still fresh (within `appOpenAdFreshnessThreshold`)
|
|
70
|
+
- **Auto-Reload Config**: New `appOpenAutoReload` setting to control automatic reloading after ad dismissal
|
|
71
|
+
- **Deprecated**: `appOpenFetchFreshAd` - migrate to `appOpenLoadingStrategy`
|
|
72
|
+
|
|
73
|
+
```kotlin
|
|
74
|
+
AdManageKitConfig.apply {
|
|
75
|
+
// Loading strategy (ON_DEMAND, ONLY_CACHE, HYBRID)
|
|
76
|
+
appOpenLoadingStrategy = AdLoadingStrategy.HYBRID
|
|
77
|
+
|
|
78
|
+
// Freshness threshold for cached ads (default: 4 hours)
|
|
79
|
+
appOpenAdFreshnessThreshold = 4.hours
|
|
80
|
+
|
|
81
|
+
// Auto-reload after ad dismissal (default: true)
|
|
82
|
+
appOpenAutoReload = true
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## What's New in 3.3.4
|
|
87
|
+
|
|
88
|
+
### Subscription Expiry Verification
|
|
89
|
+
- **Server-Side Verification**: New API to verify subscriptions and get accurate expiry dates from your backend
|
|
90
|
+
- **Expiry Methods**: `getExpiryTimeFormatted()`, `getRemainingDays()`, `isExpired()` on PurchaseResult
|
|
91
|
+
- **AppPurchase Helpers**: `getSubscriptionExpiryTime()`, `getSubscriptionRemainingDays()`, `isSubscriptionExpired()`
|
|
92
|
+
|
|
93
|
+
```kotlin
|
|
94
|
+
// Set up verification callback
|
|
95
|
+
AppPurchase.getInstance().setSubscriptionVerificationCallback { packageName, subscriptionId, purchaseToken, listener ->
|
|
96
|
+
yourApi.verifySubscription(purchaseToken) { expiryMillis ->
|
|
97
|
+
val details = SubscriptionVerificationCallback.SubscriptionDetails.Builder()
|
|
98
|
+
.setExpiryTimeMillis(expiryMillis)
|
|
99
|
+
.build()
|
|
100
|
+
listener.onVerified(details)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Verify and get expiry
|
|
105
|
+
AppPurchase.getInstance().verifySubscription("premium_monthly",
|
|
106
|
+
object : AppPurchase.SubscriptionVerificationListener {
|
|
107
|
+
override fun onVerified(subscription: PurchaseResult) {
|
|
108
|
+
val expiryDate = subscription.getExpiryTimeFormatted("dd MMM yyyy")
|
|
109
|
+
val daysLeft = subscription.getRemainingDays()
|
|
110
|
+
}
|
|
111
|
+
override fun onVerificationFailed(error: String?) { }
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## What's New in 3.3.3
|
|
117
|
+
|
|
118
|
+
### SDK-Agnostic Type Aliases
|
|
119
|
+
- **Migration Compatibility**: Callbacks use `AdKitError`, `AdKitLoadError`, `AdKitValue` type aliases
|
|
120
|
+
- **Same Signatures**: Your callback implementations work across both SDK versions
|
|
121
|
+
- **Easy Migration**: Switch between main (GMS SDK) and nextgen (Next-Gen SDK) branches without code changes
|
|
122
|
+
|
|
123
|
+
```kotlin
|
|
124
|
+
// Callbacks now use type aliases that resolve to the appropriate SDK types
|
|
125
|
+
object : AdLoadCallback() {
|
|
126
|
+
override fun onFailedToLoad(error: AdKitError?) { // Works on both branches
|
|
127
|
+
Log.e("Ads", "Failed: ${error?.message}")
|
|
128
|
+
}
|
|
129
|
+
override fun onPaidEvent(adValue: AdKitValue) { // Works on both branches
|
|
130
|
+
trackRevenue(adValue.valueMicros)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## What's New in 3.3.2
|
|
136
|
+
|
|
137
|
+
### InterstitialAdBuilder Fixes
|
|
138
|
+
- **Ad Unit Assignment**: Fixed ad unit not being assigned to AdManager on first HYBRID fetch
|
|
139
|
+
- **Immediate Availability**: `adUnit()` now sets AdManager.adUnitId immediately for first-call reliability
|
|
140
|
+
|
|
141
|
+
### everyNthTime Feature Fix
|
|
142
|
+
- **Counter Persistence**: Call counter now persists across builder instances in AdManager
|
|
143
|
+
- **Counter API**: New methods to manage counters: `getCallCount()`, `resetCallCount()`, `resetAllCallCounts()`
|
|
144
|
+
|
|
145
|
+
```kotlin
|
|
146
|
+
// everyNthTime now works correctly
|
|
147
|
+
InterstitialAdBuilder.with(activity)
|
|
148
|
+
.adUnit(adUnitId)
|
|
149
|
+
.everyNthTime(3) // Shows on 3rd, 6th, 9th calls, etc.
|
|
150
|
+
.show { navigateNext() }
|
|
151
|
+
|
|
152
|
+
// Reset counters when user upgrades
|
|
153
|
+
AdManager.getInstance().resetAllCallCounts()
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### New Native Templates
|
|
157
|
+
- **flexible**: Adaptive layout that adjusts to available space
|
|
158
|
+
- **icon_left**: Icon on left side with MediaView at top for GridView display
|
|
159
|
+
- **top_icon_media**: Icon at top, MediaView in middle, CTA at bottom
|
|
160
|
+
|
|
161
|
+
```kotlin
|
|
162
|
+
nativeTemplateView.setTemplate(NativeAdTemplate.FLEXIBLE)
|
|
163
|
+
nativeTemplateView.setTemplate(NativeAdTemplate.ICON_LEFT)
|
|
164
|
+
nativeTemplateView.setTemplate(NativeAdTemplate.TOP_ICON_MEDIA)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## What's New in 3.1.0
|
|
168
|
+
|
|
169
|
+
### FRESH_WITH_CACHE_FALLBACK Strategy Fix
|
|
170
|
+
- **Auto-Caching**: Successfully loaded ads are now properly cached for future fallback
|
|
171
|
+
- **RecyclerView Optimized**: Fresh ads build up the cache over time for better fallback availability
|
|
172
|
+
- **Complete Implementation**: Strategy now works as documented
|
|
173
|
+
|
|
174
|
+
### New Native Template
|
|
175
|
+
- **MEDIUM_HORIZONTAL**: 55% media (left) / 45% content (right) horizontal split layout
|
|
176
|
+
- **27 Total Templates**: 21 standard + 6 video templates
|
|
177
|
+
|
|
178
|
+
## What's New in 3.0.0
|
|
179
|
+
|
|
180
|
+
### Ad Pool System
|
|
181
|
+
- **Multiple Ad Units**: Load multiple interstitial ad units into a pool for maximum show rate
|
|
182
|
+
- **Auto-Selection**: Shows ANY available ad from the pool when requested
|
|
183
|
+
- **Duplicate Prevention**: Automatically skips duplicate load requests
|
|
184
|
+
|
|
185
|
+
### Smart Splash Ads
|
|
186
|
+
- **showOrWaitForAd()**: Single method handles all splash scenarios automatically
|
|
187
|
+
- **Intelligent Behavior**: Shows cached ad immediately, waits if loading, or fetches fresh
|
|
188
|
+
|
|
189
|
+
### App Open Ad Prefetching
|
|
190
|
+
- **prefetchNextAd()**: Prefetch ads before external intents for instant display on return
|
|
191
|
+
- **isAdLoading()**: Check if ad is currently being fetched
|
|
192
|
+
|
|
193
|
+
### Enhanced Analytics
|
|
194
|
+
- **Session Tracking**: Fill rate, show rate, and impression tracking
|
|
195
|
+
- **getAdStats()**: Access session-level ad performance metrics
|
|
196
|
+
|
|
197
|
+
### Technical Improvements
|
|
198
|
+
- Modern `WindowInsetsController` API (replaces deprecated systemUiVisibility)
|
|
199
|
+
- Thread-safe ad pool with `ConcurrentHashMap`
|
|
200
|
+
- Cross-ad-unit fallback for native ads
|
|
201
|
+
|
|
202
|
+
## Screenshots
|
|
203
|
+
|
|
204
|
+
| NativeBannerSmall Ad | Interstitial Ad | App Open Ad | UMP Consent Form |
|
|
205
|
+
|----------------------|-----------------|-----------------|------------------|
|
|
206
|
+
|  |  |  |  |
|
|
207
|
+
|
|
208
|
+
## Demo Video
|
|
209
|
+
|
|
210
|
+
Watch a short demo of `AdManageKit` in action:
|
|
211
|
+
|
|
212
|
+
[Watch on YouTube](https://youtube.com/shorts/h_872tOARpU)
|
|
213
|
+
|
|
214
|
+
## Getting Started
|
|
215
|
+
|
|
216
|
+
### Installation
|
|
217
|
+
|
|
218
|
+
**Step 1:** Add JitPack to your root `build.gradle`:
|
|
219
|
+
|
|
220
|
+
```groovy
|
|
221
|
+
dependencyResolutionManagement {
|
|
222
|
+
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
|
223
|
+
repositories {
|
|
224
|
+
mavenCentral()
|
|
225
|
+
maven { url 'https://jitpack.io' }
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Step 2:** Add dependencies to your app's `build.gradle`:
|
|
231
|
+
|
|
232
|
+
<table>
|
|
233
|
+
<tr>
|
|
234
|
+
<th>Main Branch (Stable GMS SDK)</th>
|
|
235
|
+
<th>Next-Gen Branch (Beta GMA SDK)</th>
|
|
236
|
+
</tr>
|
|
237
|
+
<tr>
|
|
238
|
+
<td>
|
|
239
|
+
|
|
240
|
+
```groovy
|
|
241
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v3.3.5'
|
|
242
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v3.3.5'
|
|
243
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v3.3.5'
|
|
244
|
+
|
|
245
|
+
// For Jetpack Compose support
|
|
246
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v3.3.5'
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
</td>
|
|
250
|
+
<td>
|
|
251
|
+
|
|
252
|
+
```groovy
|
|
253
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-nextgen:v4.1.1'
|
|
254
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing-nextgen:v4.1.1'
|
|
255
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core-nextgen:v4.1.1'
|
|
256
|
+
|
|
257
|
+
// For Jetpack Compose support
|
|
258
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose-nextgen:v4.1.1'
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
</td>
|
|
262
|
+
</tr>
|
|
263
|
+
</table>
|
|
264
|
+
|
|
265
|
+
**Step 3:** Sync your project with Gradle.
|
|
266
|
+
|
|
267
|
+
## Features
|
|
268
|
+
|
|
269
|
+
### NativeTemplateView (v2.6.0+)
|
|
270
|
+
- **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
|
|
271
|
+
- **XML & Programmatic**: Set templates via `app:adTemplate` or `setTemplate()`
|
|
272
|
+
- **Material 3 Theming**: Automatic dark/light mode support
|
|
273
|
+
- **AdChoices Control**: Configure placement position (v2.9.0+)
|
|
274
|
+
- **Video-Ready**: All templates support video ads (120dp+ MediaView)
|
|
275
|
+
- [View Documentation](docs/NATIVE_TEMPLATE_VIEW.md)
|
|
276
|
+
|
|
277
|
+
### Ad Loading Strategies (v2.6.0+)
|
|
278
|
+
- **ON_DEMAND**: Fetch fresh ads with loading dialog
|
|
279
|
+
- **ONLY_CACHE**: Instant display from cache
|
|
280
|
+
- **HYBRID**: Cache-first with fallback fetch (recommended)
|
|
281
|
+
- [View Documentation](docs/AD_LOADING_STRATEGIES.md)
|
|
282
|
+
|
|
283
|
+
### Jetpack Compose Integration
|
|
284
|
+
- BannerAdCompose, NativeAdCompose, InterstitialAdCompose
|
|
285
|
+
- Programmatic native ads without predefined layouts
|
|
286
|
+
- ConditionalAd, CacheWarmingEffect utilities
|
|
287
|
+
|
|
288
|
+
### AdMob Ads Management
|
|
289
|
+
- **Banner Ads**: Auto-refresh, collapsible banners, smart retry
|
|
290
|
+
- **Native Ads**: Small, Medium, Large formats with caching
|
|
291
|
+
- **Interstitial Ads**: Time/count-based triggers, dialog support
|
|
292
|
+
- **App Open Ads**: Lifecycle-aware with activity exclusion
|
|
293
|
+
|
|
294
|
+
### Centralized Configuration
|
|
295
|
+
- **AdManageKitConfig**: Single configuration point
|
|
296
|
+
- Environment-specific settings (debug vs production)
|
|
297
|
+
- Runtime configuration changes
|
|
298
|
+
|
|
299
|
+
### Intelligent Native Ad Caching
|
|
300
|
+
- Screen-aware caching prevents collisions
|
|
301
|
+
- Smart preloading with usage patterns
|
|
302
|
+
- LRU cache with configurable expiration
|
|
303
|
+
|
|
304
|
+
### Reliability & Performance
|
|
305
|
+
- Smart retry with exponential backoff
|
|
306
|
+
- Circuit breaker for failing ad units
|
|
307
|
+
- Memory leak prevention with WeakReference
|
|
308
|
+
|
|
309
|
+
### Privacy & Compliance
|
|
310
|
+
- UMP consent management (GDPR/CCPA)
|
|
311
|
+
- Automatic ad hiding for purchased users
|
|
312
|
+
|
|
313
|
+
### Multi-Module Architecture
|
|
314
|
+
- **Core Module**: Shared interfaces and configuration
|
|
315
|
+
- **Compose Module**: Jetpack Compose integration
|
|
316
|
+
- **Billing Module**: Google Play Billing Library v8
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Usage Guide
|
|
321
|
+
|
|
322
|
+
### Quick Configuration
|
|
323
|
+
|
|
324
|
+
Configure AdManageKit in your Application class:
|
|
325
|
+
|
|
326
|
+
```kotlin
|
|
327
|
+
class MyApp : Application() {
|
|
328
|
+
private lateinit var appOpenManager: AppOpenManager
|
|
329
|
+
|
|
330
|
+
override fun onCreate() {
|
|
331
|
+
super.onCreate()
|
|
332
|
+
|
|
333
|
+
// Configure AdManageKit
|
|
334
|
+
AdManageKitConfig.apply {
|
|
335
|
+
debugMode = BuildConfig.DEBUG
|
|
336
|
+
enableSmartPreloading = true
|
|
337
|
+
autoRetryFailedAds = true
|
|
338
|
+
|
|
339
|
+
// Ad Loading Strategies (v2.6.0+)
|
|
340
|
+
interstitialLoadingStrategy = AdLoadingStrategy.HYBRID
|
|
341
|
+
appOpenLoadingStrategy = AdLoadingStrategy.HYBRID
|
|
342
|
+
nativeLoadingStrategy = AdLoadingStrategy.HYBRID
|
|
343
|
+
|
|
344
|
+
// Auto-reload ads after showing (v2.7.0+)
|
|
345
|
+
interstitialAutoReload = true // default: true
|
|
346
|
+
appOpenAutoReload = true // default: true
|
|
347
|
+
rewardedAutoReload = true // default: true
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Set up billing
|
|
351
|
+
BillingConfig.setPurchaseProvider(BillingPurchaseProvider())
|
|
352
|
+
|
|
353
|
+
// Initialize app open ads
|
|
354
|
+
appOpenManager = AppOpenManager(this, "your-app-open-ad-unit-id")
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### NativeTemplateView (v2.6.0+)
|
|
360
|
+
|
|
361
|
+
#### XML Usage
|
|
362
|
+
|
|
363
|
+
```xml
|
|
364
|
+
<com.i2hammad.admanagekit.admob.NativeTemplateView
|
|
365
|
+
android:id="@+id/nativeTemplateView"
|
|
366
|
+
android:layout_width="match_parent"
|
|
367
|
+
android:layout_height="wrap_content"
|
|
368
|
+
app:adTemplate="material3" />
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
#### Available Templates
|
|
372
|
+
|
|
373
|
+
| Template | Best For |
|
|
374
|
+
|----------|----------|
|
|
375
|
+
| `card_modern` | General use |
|
|
376
|
+
| `material3` | M3 apps |
|
|
377
|
+
| `minimal` | Content-focused |
|
|
378
|
+
| `compact_horizontal` | Lists |
|
|
379
|
+
| `list_item` | RecyclerView items |
|
|
380
|
+
| `magazine` | News/blog apps |
|
|
381
|
+
| `app_store` | App promotion (v2.9.0+) |
|
|
382
|
+
| `social_feed` | Feed integration (v2.9.0+) |
|
|
383
|
+
| `gradient_card` | Premium feel (v2.9.0+) |
|
|
384
|
+
| `pill_banner` | Inline placement (v2.9.0+) |
|
|
385
|
+
| `medium_horizontal` | 55/45 media-content split (v3.0.0+) |
|
|
386
|
+
| `spotlight` | High visibility (v2.9.0+) |
|
|
387
|
+
| `media_content_split` | Balanced display (v2.9.0+) |
|
|
388
|
+
| `flexible` | Adaptive layout (v3.3.2+) |
|
|
389
|
+
| `icon_left` | Icon on left, GridView optimized (v3.3.2+) |
|
|
390
|
+
| `top_icon_media` | Icon at top, MediaView center (v3.3.2+) |
|
|
391
|
+
| `video_small/medium/large` | Video content |
|
|
392
|
+
| `video_square/vertical/fullscreen` | Social feeds |
|
|
393
|
+
|
|
394
|
+
#### Programmatic Usage
|
|
395
|
+
|
|
396
|
+
```kotlin
|
|
397
|
+
// Load with default template
|
|
398
|
+
nativeTemplateView.loadNativeAd(activity, "ca-app-pub-xxx/yyy")
|
|
399
|
+
|
|
400
|
+
// Change template
|
|
401
|
+
nativeTemplateView.setTemplate(NativeAdTemplate.MAGAZINE)
|
|
402
|
+
nativeTemplateView.loadNativeAd(activity, "ca-app-pub-xxx/yyy")
|
|
403
|
+
|
|
404
|
+
// With callback
|
|
405
|
+
nativeTemplateView.loadNativeAd(activity, adUnitId, object : AdLoadCallback() {
|
|
406
|
+
override fun onAdLoaded() { /* success */ }
|
|
407
|
+
override fun onFailedToLoad(error: AdError?) { /* error */ }
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
// With strategy override
|
|
411
|
+
nativeTemplateView.loadNativeAd(activity, adUnitId, callback, AdLoadingStrategy.ONLY_CACHE)
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Banner Ads
|
|
415
|
+
|
|
416
|
+
```xml
|
|
417
|
+
<com.i2hammad.admanagekit.admob.BannerAdView
|
|
418
|
+
android:id="@+id/bannerAdView"
|
|
419
|
+
android:layout_width="match_parent"
|
|
420
|
+
android:layout_height="wrap_content" />
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
```kotlin
|
|
424
|
+
bannerAdView.loadBanner(this, "ca-app-pub-xxx/yyy")
|
|
425
|
+
// Collapsible banner
|
|
426
|
+
bannerAdView.loadCollapsibleBanner(this, "ca-app-pub-xxx/yyy", true)
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Native Ads (Traditional Views)
|
|
430
|
+
|
|
431
|
+
```xml
|
|
432
|
+
<com.i2hammad.admanagekit.admob.NativeBannerSmall
|
|
433
|
+
android:id="@+id/nativeBannerSmall"
|
|
434
|
+
android:layout_width="match_parent"
|
|
435
|
+
android:layout_height="wrap_content" />
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
```kotlin
|
|
439
|
+
nativeBannerSmall.loadNativeBannerAd(this, "ca-app-pub-xxx/yyy")
|
|
440
|
+
// With caching
|
|
441
|
+
nativeBannerSmall.loadNativeBannerAd(activity, adUnitId, useCachedAd = true)
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Interstitial Ads
|
|
445
|
+
|
|
446
|
+
```kotlin
|
|
447
|
+
// Load
|
|
448
|
+
AdManager.getInstance().loadInterstitialAd(this, "ca-app-pub-xxx/yyy")
|
|
449
|
+
|
|
450
|
+
// Show immediately
|
|
451
|
+
AdManager.getInstance().forceShowInterstitial(this, object : AdManagerCallback() {
|
|
452
|
+
override fun onNextAction() { navigateNext() }
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
// Show with dialog
|
|
456
|
+
AdManager.getInstance().forceShowInterstitialWithDialog(this, callback)
|
|
457
|
+
|
|
458
|
+
// Time-based (every 15 seconds)
|
|
459
|
+
AdManager.getInstance().showInterstitialAdByTime(this, callback)
|
|
460
|
+
|
|
461
|
+
// Count-based
|
|
462
|
+
AdManager.getInstance().showInterstitialAdByCount(this, callback, maxDisplayCount = 3)
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
#### Auto-Reload Configuration (v2.7.0+)
|
|
466
|
+
|
|
467
|
+
Control whether interstitial ads automatically reload after being shown:
|
|
468
|
+
|
|
469
|
+
```kotlin
|
|
470
|
+
// Global config (applies to all AdManager methods)
|
|
471
|
+
AdManageKitConfig.interstitialAutoReload = false // Disable auto-reload
|
|
472
|
+
|
|
473
|
+
// Per-call override via InterstitialAdBuilder
|
|
474
|
+
InterstitialAdBuilder.with(activity)
|
|
475
|
+
.adUnit(adUnitId)
|
|
476
|
+
.autoReload(true) // Override global setting for this call
|
|
477
|
+
.show { navigateNext() }
|
|
478
|
+
|
|
479
|
+
// Per-call override via AdManager
|
|
480
|
+
AdManager.getInstance().showInterstitialIfReady(activity, callback, reloadAd = false)
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
**Priority:** `InterstitialAdBuilder.autoReload()` > `AdManageKitConfig.interstitialAutoReload`
|
|
484
|
+
|
|
485
|
+
### Rewarded Ads
|
|
486
|
+
|
|
487
|
+
```kotlin
|
|
488
|
+
// Initialize once (e.g., in Application.onCreate())
|
|
489
|
+
RewardedAdManager.initialize(context, "ca-app-pub-xxx/yyy")
|
|
490
|
+
|
|
491
|
+
// Show when ready
|
|
492
|
+
if (RewardedAdManager.isAdLoaded()) {
|
|
493
|
+
RewardedAdManager.showAd(activity, object : RewardedAdManager.RewardedAdCallback {
|
|
494
|
+
override fun onRewardEarned(rewardType: String, rewardAmount: Int) {
|
|
495
|
+
grantReward(rewardType, rewardAmount)
|
|
496
|
+
}
|
|
497
|
+
override fun onAdDismissed() {
|
|
498
|
+
continueGameFlow()
|
|
499
|
+
}
|
|
500
|
+
})
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Load with timeout (splash screens)
|
|
504
|
+
RewardedAdManager.loadRewardedAdWithTimeout(context, 5000, callback)
|
|
505
|
+
|
|
506
|
+
// Preload during natural pauses
|
|
507
|
+
RewardedAdManager.preload(context)
|
|
508
|
+
|
|
509
|
+
// Control auto-reload
|
|
510
|
+
AdManageKitConfig.rewardedAutoReload = false // Disable globally
|
|
511
|
+
RewardedAdManager.showAd(activity, callback, autoReload = false) // Per-call override
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### App Open Ads
|
|
515
|
+
|
|
516
|
+
```kotlin
|
|
517
|
+
// In Application class
|
|
518
|
+
appOpenManager = AppOpenManager(this, "ca-app-pub-xxx/yyy")
|
|
519
|
+
|
|
520
|
+
// Exclude activities
|
|
521
|
+
appOpenManager.disableAppOpenWithActivity(MainActivity::class.java)
|
|
522
|
+
|
|
523
|
+
// Force show
|
|
524
|
+
appOpenManager.forceShowAdIfAvailable(activity, callback)
|
|
525
|
+
|
|
526
|
+
// Skip next ad
|
|
527
|
+
appOpenManager.skipNextAd()
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
#### Single-Activity App Support (v3.2.0+)
|
|
531
|
+
|
|
532
|
+
For apps with one activity and multiple fragments:
|
|
533
|
+
|
|
534
|
+
```kotlin
|
|
535
|
+
// Set current screen when navigating
|
|
536
|
+
navController.addOnDestinationChangedListener { _, destination, _ ->
|
|
537
|
+
appOpenManager.setCurrentScreenTag(destination.label?.toString())
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Exclude specific screens
|
|
541
|
+
appOpenManager.excludeScreenTags("Payment", "Onboarding", "Checkout")
|
|
542
|
+
|
|
543
|
+
// Or use fragment tag provider
|
|
544
|
+
appOpenManager.setFragmentTagProvider {
|
|
545
|
+
supportFragmentManager.fragments.lastOrNull()?.tag
|
|
546
|
+
}
|
|
547
|
+
appOpenManager.excludeFragmentTags("PaymentFragment", "OnboardingFragment")
|
|
548
|
+
|
|
549
|
+
// Temporarily disable during critical flows
|
|
550
|
+
appOpenManager.disableAppOpenAdsTemporarily()
|
|
551
|
+
// ... perform operation ...
|
|
552
|
+
appOpenManager.enableAppOpenAds()
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### Jetpack Compose
|
|
556
|
+
|
|
557
|
+
```kotlin
|
|
558
|
+
@Composable
|
|
559
|
+
fun MyScreen() {
|
|
560
|
+
// Banner
|
|
561
|
+
BannerAdCompose(adUnitId = "ca-app-pub-xxx/yyy")
|
|
562
|
+
|
|
563
|
+
// NativeTemplateView with any template (v2.6.0+)
|
|
564
|
+
NativeTemplateCompose(
|
|
565
|
+
adUnitId = "ca-app-pub-xxx/yyy",
|
|
566
|
+
template = NativeAdTemplate.MATERIAL3,
|
|
567
|
+
loadingStrategy = AdLoadingStrategy.HYBRID
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
// Native with loading strategy (ON_DEMAND or HYBRID only)
|
|
571
|
+
NativeBannerMediumCompose(
|
|
572
|
+
adUnitId = "ca-app-pub-xxx/yyy",
|
|
573
|
+
loadingStrategy = AdLoadingStrategy.HYBRID
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
// Interstitial
|
|
577
|
+
val showInterstitial = rememberInterstitialAd(
|
|
578
|
+
adUnitId = "ca-app-pub-xxx/yyy",
|
|
579
|
+
preloadAd = true
|
|
580
|
+
)
|
|
581
|
+
Button(onClick = { showInterstitial() }) {
|
|
582
|
+
Text("Show Ad")
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Conditional (hides for purchased users)
|
|
586
|
+
ConditionalAd {
|
|
587
|
+
ProgrammaticNativeBannerMediumCompose(adUnitId = "ca-app-pub-xxx/yyy")
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### UMP Consent
|
|
593
|
+
|
|
594
|
+
```kotlin
|
|
595
|
+
AdsConsentManager.getInstance(this).requestUMP(
|
|
596
|
+
activity = this,
|
|
597
|
+
isDebug = true,
|
|
598
|
+
testDeviceId = "TEST_DEVICE_ID",
|
|
599
|
+
resetConsent = false,
|
|
600
|
+
listener = object : UMPResultListener {
|
|
601
|
+
override fun onCheckUMPSuccess(isConsentGiven: Boolean) {
|
|
602
|
+
if (isConsentGiven) {
|
|
603
|
+
// Initialize and load ads here
|
|
604
|
+
AdManager.getInstance().loadInterstitialAd(activity, adUnitId)
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
)
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
### In-App Purchases (v2.9.0+)
|
|
612
|
+
|
|
613
|
+
```kotlin
|
|
614
|
+
// Define products with categories
|
|
615
|
+
val products = listOf(
|
|
616
|
+
PurchaseItem("coins_100", TYPE_IAP.PURCHASE, PurchaseCategory.CONSUMABLE),
|
|
617
|
+
PurchaseItem("remove_ads", TYPE_IAP.PURCHASE, PurchaseCategory.REMOVE_ADS),
|
|
618
|
+
PurchaseItem("lifetime", TYPE_IAP.PURCHASE, PurchaseCategory.LIFETIME_PREMIUM),
|
|
619
|
+
PurchaseItem("premium_monthly", "free_trial", TYPE_IAP.SUBSCRIPTION)
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
// Initialize
|
|
623
|
+
AppPurchase.getInstance().initBilling(application, products)
|
|
624
|
+
|
|
625
|
+
// Purchase
|
|
626
|
+
AppPurchase.getInstance().purchase(activity, "remove_ads")
|
|
627
|
+
|
|
628
|
+
// Subscribe
|
|
629
|
+
AppPurchase.getInstance().subscribe(activity, "premium_monthly")
|
|
630
|
+
|
|
631
|
+
// Check status
|
|
632
|
+
if (AppPurchase.getInstance().isPurchased()) {
|
|
633
|
+
// User has premium (subscription, lifetime, or remove_ads)
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Track purchases and handle consumables
|
|
637
|
+
AppPurchase.getInstance().setPurchaseHistoryListener(object : PurchaseHistoryListener {
|
|
638
|
+
override fun onNewPurchase(productId: String, purchase: PurchaseResult) {
|
|
639
|
+
if (productId == "coins_100") {
|
|
640
|
+
addCoins(100 * purchase.quantity)
|
|
641
|
+
AppPurchase.getInstance().consumePurchase(productId) // Manual consume
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
override fun onPurchaseConsumed(productId: String, purchase: PurchaseResult) { }
|
|
645
|
+
})
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
#### Subscription Management (v2.9.0+)
|
|
649
|
+
|
|
650
|
+
```kotlin
|
|
651
|
+
// Check subscription state
|
|
652
|
+
val state = AppPurchase.getInstance().getSubscriptionState("premium_monthly")
|
|
653
|
+
when (state) {
|
|
654
|
+
SubscriptionState.ACTIVE -> showPremiumUI()
|
|
655
|
+
SubscriptionState.CANCELLED -> showRenewalPrompt() // Still has access
|
|
656
|
+
SubscriptionState.EXPIRED -> showSubscribeButton()
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Upgrade subscription
|
|
660
|
+
AppPurchase.getInstance().upgradeSubscription(activity, "premium_yearly")
|
|
661
|
+
|
|
662
|
+
// Downgrade subscription
|
|
663
|
+
AppPurchase.getInstance().downgradeSubscription(activity, "premium_basic")
|
|
664
|
+
|
|
665
|
+
// Full control with proration mode
|
|
666
|
+
AppPurchase.getInstance().changeSubscription(
|
|
667
|
+
activity,
|
|
668
|
+
"premium_monthly",
|
|
669
|
+
"premium_yearly",
|
|
670
|
+
SubscriptionReplacementMode.CHARGE_PRORATED_PRICE
|
|
671
|
+
)
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
---
|
|
675
|
+
|
|
676
|
+
## Documentation
|
|
677
|
+
|
|
678
|
+
- [NativeTemplateView Guide](docs/NATIVE_TEMPLATE_VIEW.md)
|
|
679
|
+
- [Ad Loading Strategies](docs/AD_LOADING_STRATEGIES.md)
|
|
680
|
+
- [Jetpack Compose Integration](docs/COMPOSE_INTEGRATION.md)
|
|
681
|
+
- [Native Ads Caching](docs/native-ads-caching.md)
|
|
682
|
+
- [Interstitial Ads](docs/interstitial-ads.md)
|
|
683
|
+
- [Rewarded Ads](docs/rewarded-ads.md)
|
|
684
|
+
- [App Open Ads](docs/app-open-ads.md)
|
|
685
|
+
- [Billing Integration Guide](docs/APP_PURCHASE_GUIDE.md)
|
|
686
|
+
- [Release Notes v3.3.5](docs/release-notes/RELEASE_NOTES_v3.3.5.md)
|
|
687
|
+
- [Release Notes v3.3.4](docs/release-notes/RELEASE_NOTES_v3.3.4.md)
|
|
688
|
+
- [Release Notes v3.3.3](docs/release-notes/RELEASE_NOTES_v3.3.3.md)
|
|
689
|
+
- [Release Notes v3.3.2](docs/release-notes/RELEASE_NOTES_v3.3.2.md)
|
|
690
|
+
- [Release Notes v3.3.0](docs/release-notes/RELEASE_NOTES_v3.3.0.md)
|
|
691
|
+
- [Release Notes v3.1.0](docs/release-notes/RELEASE_NOTES_v3.1.0.md)
|
|
692
|
+
- [Release Notes v3.0.0](docs/release-notes/RELEASE_NOTES_v3.0.0.md)
|
|
693
|
+
- [API Reference](docs/API_REFERENCE.md)
|
|
694
|
+
|
|
695
|
+
### Wiki
|
|
696
|
+
- [Rewarded Ads](wiki/Rewarded-Ads.md)
|
|
697
|
+
- [Billing Integration](wiki/Billing-Integration.md)
|
|
698
|
+
- [Purchase Categories](wiki/Purchase-Categories.md)
|
|
699
|
+
- [Consumable Products](wiki/Consumable-Products.md)
|
|
700
|
+
- [Subscriptions](wiki/Subscriptions.md)
|
|
701
|
+
- [Subscription Upgrades](wiki/Subscription-Upgrades.md)
|
|
702
|
+
|
|
703
|
+
### API Documentation (Dokka)
|
|
704
|
+
|
|
705
|
+
**Online:** [https://i2hammad.github.io/AdManageKit/](https://i2hammad.github.io/AdManageKit/)
|
|
706
|
+
|
|
707
|
+
Generate locally:
|
|
708
|
+
|
|
709
|
+
```bash
|
|
710
|
+
./gradlew dokkaHtmlMultiModule
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
Output: `build/dokka/htmlMultiModule/index.html`
|
|
714
|
+
|
|
715
|
+
---
|
|
716
|
+
|
|
717
|
+
## Migration Guide
|
|
718
|
+
|
|
719
|
+
### Migrating to 3.0.0
|
|
720
|
+
|
|
721
|
+
Version 3.0.0 is **fully backward compatible**. Optionally adopt new features:
|
|
722
|
+
|
|
723
|
+
#### Smart Splash Ads (Recommended)
|
|
724
|
+
|
|
725
|
+
Replace separate load + show calls with single `showOrWaitForAd()`:
|
|
726
|
+
|
|
727
|
+
```kotlin
|
|
728
|
+
// Before (v2.9.0) - Two-step approach
|
|
729
|
+
AdManager.getInstance().loadInterstitialAdForSplash(this, adUnitId, 10_000, object : AdManagerCallback() {
|
|
730
|
+
override fun onNextAction() {
|
|
731
|
+
AdManager.getInstance().forceShowInterstitial(this@SplashActivity, callback)
|
|
732
|
+
}
|
|
733
|
+
})
|
|
734
|
+
|
|
735
|
+
// After (v3.0.0) - Single smart call
|
|
736
|
+
AdManager.getInstance().showOrWaitForAd(
|
|
737
|
+
activity = this,
|
|
738
|
+
callback = object : AdManagerCallback() {
|
|
739
|
+
override fun onNextAction() { navigateNext() }
|
|
740
|
+
},
|
|
741
|
+
timeoutMillis = 10_000
|
|
742
|
+
)
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
#### Ad Pool for Higher Show Rate
|
|
746
|
+
|
|
747
|
+
```kotlin
|
|
748
|
+
// Before (single ad unit)
|
|
749
|
+
AdManager.getInstance().loadInterstitialAd(context, "single_unit")
|
|
750
|
+
|
|
751
|
+
// After (multiple ad units for redundancy)
|
|
752
|
+
AdManager.getInstance().loadMultipleAdUnits(context, "high_ecpm", "medium_ecpm", "fallback")
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
#### App Open Ad Prefetching
|
|
756
|
+
|
|
757
|
+
```kotlin
|
|
758
|
+
// Prefetch before external intents
|
|
759
|
+
appOpenManager.prefetchNextAd()
|
|
760
|
+
startActivityForResult(cameraIntent, REQUEST_CODE)
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
### Migrating to 2.9.0
|
|
764
|
+
|
|
765
|
+
Version 2.9.0 has **one breaking change** for consumable products:
|
|
766
|
+
|
|
767
|
+
**Consumables are no longer auto-consumed.** You must manually call `consumePurchase()`:
|
|
768
|
+
|
|
769
|
+
```kotlin
|
|
770
|
+
// Before v2.9.0 (auto-consume)
|
|
771
|
+
AppPurchase.getInstance().setConsumePurchase(true) // Deprecated
|
|
772
|
+
|
|
773
|
+
// After v2.9.0 (manual consume)
|
|
774
|
+
AppPurchase.getInstance().setPurchaseHistoryListener(object : PurchaseHistoryListener {
|
|
775
|
+
override fun onNewPurchase(productId: String, purchase: PurchaseResult) {
|
|
776
|
+
grantItems(productId, purchase.quantity)
|
|
777
|
+
AppPurchase.getInstance().consumePurchase(productId) // Manual!
|
|
778
|
+
}
|
|
779
|
+
override fun onPurchaseConsumed(productId: String, purchase: PurchaseResult) { }
|
|
780
|
+
})
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
**Use Purchase Categories** for better product classification:
|
|
784
|
+
|
|
785
|
+
```kotlin
|
|
786
|
+
// Before
|
|
787
|
+
PurchaseItem("coins", TYPE_IAP.PURCHASE, true) // isConsumable
|
|
788
|
+
|
|
789
|
+
// After (explicit categories)
|
|
790
|
+
PurchaseItem("coins", TYPE_IAP.PURCHASE, PurchaseCategory.CONSUMABLE)
|
|
791
|
+
PurchaseItem("remove_ads", TYPE_IAP.PURCHASE, PurchaseCategory.REMOVE_ADS)
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
### Migrating to 2.8.0
|
|
795
|
+
|
|
796
|
+
Version 2.8.0 is **fully backward compatible** with one behavioral change:
|
|
797
|
+
|
|
798
|
+
**`forceShowInterstitial()` now respects loading strategy:**
|
|
799
|
+
```kotlin
|
|
800
|
+
// If you need old behavior (always force fetch), use:
|
|
801
|
+
AdManager.getInstance().forceShowInterstitialAlways(activity, callback)
|
|
802
|
+
|
|
803
|
+
// Or set strategy to ON_DEMAND globally:
|
|
804
|
+
AdManageKitConfig.interstitialLoadingStrategy = AdLoadingStrategy.ON_DEMAND
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
### Migrating to 2.7.0
|
|
808
|
+
|
|
809
|
+
Version 2.7.0 is **fully backward compatible**. Optionally adopt new features:
|
|
810
|
+
|
|
811
|
+
```kotlin
|
|
812
|
+
// Old way (still works)
|
|
813
|
+
val nativeBannerMedium = NativeBannerMedium(context)
|
|
814
|
+
nativeBannerMedium.loadNativeBannerAd(activity, adUnitId)
|
|
815
|
+
|
|
816
|
+
// New unified approach
|
|
817
|
+
val nativeTemplateView = NativeTemplateView(context)
|
|
818
|
+
nativeTemplateView.setTemplate(NativeAdTemplate.CARD_MODERN)
|
|
819
|
+
nativeTemplateView.loadNativeAd(activity, adUnitId)
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
---
|
|
823
|
+
|
|
824
|
+
## Sample Project
|
|
825
|
+
|
|
826
|
+
The `app` module demonstrates all features. To run:
|
|
827
|
+
|
|
828
|
+
1. Clone: `git clone https://github.com/i2hammad/AdManageKit.git`
|
|
829
|
+
2. Open in Android Studio
|
|
830
|
+
3. Replace placeholder AdMob IDs
|
|
831
|
+
4. Run on device or emulator
|
|
832
|
+
|
|
833
|
+
---
|
|
834
|
+
|
|
835
|
+
## Contributing
|
|
836
|
+
|
|
837
|
+
1. Fork the repository
|
|
838
|
+
2. Create a branch (`git checkout -b feature/YourFeature`)
|
|
839
|
+
3. Commit changes (`git commit -m 'Add YourFeature'`)
|
|
840
|
+
4. Push (`git push origin feature/YourFeature`)
|
|
841
|
+
5. Open a Pull Request
|
|
842
|
+
|
|
843
|
+
## License
|
|
844
|
+
|
|
845
|
+
Licensed under the MIT License. See [LICENSE](LICENSE).
|
|
846
|
+
|
|
847
|
+
## Support
|
|
848
|
+
|
|
849
|
+
[Buy me a coffee](https://buymeacoffee.com/i2hammad)
|
|
850
|
+
|
|
851
|
+
For issues: [GitHub Issues](https://github.com/i2hammad/AdManageKit/issues) or [hammadmughal0001@gmail.com](mailto:hammadmughal0001@gmail.com)
|