admanagekit-mcp-server 1.1.1 → 1.2.1
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 +15 -9
- package/content/docs/AD_LOADING_STRATEGIES.md +34 -3
- package/content/docs/COMPOSE_INTEGRATION.md +79 -5
- package/content/docs/NATIVE_AD_PRELOADING.md +5 -2
- package/content/docs/NATIVE_TEMPLATE_VIEW.md +7 -3
- package/content/docs/release-notes/RELEASE_NOTES_v4.4.1.md +110 -0
- package/content/docs/release-notes/RELEASE_NOTES_v4.4.2.md +121 -0
- package/content/wiki/Ad-Loading-Strategies.md +26 -8
- package/content/wiki/App-Open-Ads.md +5 -5
- package/content/wiki/Banner-Ads.md +4 -4
- package/content/wiki/Billing-Integration.md +2 -2
- package/content/wiki/Configuration.md +25 -1
- package/content/wiki/Home.md +70 -18
- package/content/wiki/Interstitial-Ads.md +9 -6
- package/content/wiki/Jetpack-Compose.md +26 -5
- package/content/wiki/Multi-Provider-Waterfall.md +4 -4
- package/content/wiki/NativeAdManager.md +38 -9
- package/content/wiki/Yandex-Integration.md +3 -3
- package/dist/tools/code-generation.js +19 -10
- package/dist/tools/code-generation.js.map +1 -1
- package/dist/utils/templates.js +350 -33
- package/dist/utils/templates.js.map +1 -1
- package/package.json +1 -1
package/content/README.md
CHANGED
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
|
|
6
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
7
|
|
|
8
|
-
**Latest Version `4.4.
|
|
8
|
+
**Latest Version `4.4.2`** is a bug-fix release. No API changed, but several fixes are user-visible and two affect revenue: rewarded ads could **crash the app** (every show-path callback, including `onRewardEarned`, was delivered on a background thread), a **completed purchase could fail to disable ads** until the next launch, **blank gaps** were left where banner/native slots should have collapsed for premium users and after failed loads (in both XML and Compose), and app open ads could appear over screens excluded via `disableAppOpenAdsTemporarily()`. Also fixes a `BannerAdView` leak that kept requesting ads after detach. Two deliberate behavior changes: an account-hold subscription no longer disables ads, and premium users no longer reserve ad space in Compose. See [Release Notes v4.4.2](docs/release-notes/RELEASE_NOTES_v4.4.2.md).
|
|
9
|
+
|
|
10
|
+
**Version `4.4.1`** is a patch release that updates the Google Mobile Ads Next-Gen SDK to **1.3.0** (from 1.2.1) and repairs three documentation-delivery channels that had silently gone stale: the API docs build had failed on every release since the Dokka 2.x upgrade, leaving [the published KDoc](https://i2hammad.github.io/AdManageKit/) without any 4.x types; MCP server npm publishing had been broken since January 2026; and the MCP tool schemas hardcoded which versions and classes existed, so they rejected `4.4.0` as an unknown version. No AdManageKit API changed. See [Release Notes v4.4.1](docs/release-notes/RELEASE_NOTES_v4.4.1.md).
|
|
11
|
+
|
|
12
|
+
**Version `4.4.0`** is a minor release focused on the billing module. Subscription offers can now be **purchased individually** — `subscribe(activity, offer)` buys exactly the plan the user tapped, where previously the library always resolved the token itself and could charge for whichever offer Play listed last. It also adds offer lookup by id/base plan/tag, cross-cadence price normalization (`BillingPeriod`, `getSavingsPercent`, `getFormattedPricePerMonth`), trial-eligibility checks, Play Billing 9 one-time product offers (discounts, rentals, pre-orders, limited quantity), client-side **account hold** detection, Play payment-recovery messaging, and product-details diagnostics. Purely additive, with one behavior change: an on-hold subscription now reports `ON_HOLD` and is no longer `isSubscriptionActive()`. See [Release Notes v4.4.0](docs/release-notes/RELEASE_NOTES_v4.4.0.md).
|
|
9
13
|
|
|
10
14
|
**Version `4.3.5`** is a patch release that makes `appOpenAdFreshnessThreshold` (default 4 hours) actually enforced on every app open show path. Previously only `ON_DEMAND` consulted it — `HYBRID`, `ONLY_CACHE`, and the multi-provider waterfall checked only whether an ad object existed and could show a day-old cached ad. Stale ads are now discarded and replaced instead of shown. It also settles the `appOpenAdTimeout` default at **10 seconds** (the property said 4s while `resetToDefaults()` said 10s — 4s was too short for the fetch-with-dialog paths). No API changes. See [Release Notes v4.3.5](docs/release-notes/RELEASE_NOTES_v4.3.5.md).
|
|
11
15
|
|
|
@@ -25,7 +29,7 @@ Since **4.2.0** the library runs on the Google Mobile Ads **Next-Gen SDK** (`ads
|
|
|
25
29
|
|
|
26
30
|
## Next-Gen GMA SDK
|
|
27
31
|
|
|
28
|
-
As of v4.2.0, AdManageKit runs on Google's **Next-Gen Google Mobile Ads SDK** (`com.google.android.libraries.ads.mobile.sdk`, stable `1.
|
|
32
|
+
As of v4.2.0, AdManageKit runs on Google's **Next-Gen Google Mobile Ads SDK** (`com.google.android.libraries.ads.mobile.sdk`, stable `1.3.0` as of 4.4.1) instead of the legacy `com.google.android.gms:play-services-ads`. This isn't a branch or an opt-in — it's the only version of AdManageKit going forward.
|
|
29
33
|
|
|
30
34
|
### Why the move
|
|
31
35
|
|
|
@@ -122,7 +126,7 @@ See [Release Notes v4.4.0](docs/release-notes/RELEASE_NOTES_v4.4.0.md) and the [
|
|
|
122
126
|
`BannerAdView` and `BannerAdCompose` now support every standard AdMob banner size via the new `BannerAdSize` enum — `ADAPTIVE` (default, unchanged behavior), `BANNER` (320x50), `LARGE_BANNER` (320x100), `MEDIUM_RECTANGLE` (300x250), `FULL_BANNER` (468x60), `LEADERBOARD` (728x90). Select programmatically (`loadBanner(activity, adUnitId, BannerAdSize.MEDIUM_RECTANGLE)`), in XML (`app:bannerAdSize="medium_rectangle"`), or in Compose (`adSize = BannerAdSize.MEDIUM_RECTANGLE`). The size carries through retries, auto-refresh, and the multi-provider waterfall. See the [Banner Ad Guide](docs/BANNER_AD_IMPROVEMENTS.md).
|
|
123
127
|
|
|
124
128
|
### Custom Native Ad Templates
|
|
125
|
-
If none of the
|
|
129
|
+
If none of the 38 built-in `NativeTemplateView` presets fit your design, supply your own layout: `setCustomTemplate(layoutResId, shimmerResId, sizeHint)` programmatically, `app:customAdLayout`/`app:customAdShimmerLayout` in XML, or `customLayoutResId` on `NativeTemplateCompose`. The layout's root must be a Next-Gen SDK `NativeAdView` reusing the standard asset ids. See the [NativeTemplateView guide](docs/NATIVE_TEMPLATE_VIEW.md#custom-templates).
|
|
126
130
|
|
|
127
131
|
### Redesigned Banner Shimmer + Night Mode
|
|
128
132
|
The banner loading placeholder now adapts to the requested size (a 300x250 request shows a proper media-block placeholder, not a lone 50dp row), reserves the exact ad size on every path including waterfalls (no layout jump), centers to match the loaded ad, and follows the system dark theme instead of rendering a hardcoded white card.
|
|
@@ -189,15 +193,15 @@ dependencyResolutionManagement {
|
|
|
189
193
|
**Step 2:** Add dependencies to your app's `build.gradle`:
|
|
190
194
|
|
|
191
195
|
```groovy
|
|
192
|
-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v4.4.
|
|
193
|
-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v4.4.
|
|
194
|
-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.
|
|
196
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v4.4.2'
|
|
197
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v4.4.2'
|
|
198
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.2'
|
|
195
199
|
|
|
196
200
|
// For Jetpack Compose support
|
|
197
|
-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v4.4.
|
|
201
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v4.4.2'
|
|
198
202
|
|
|
199
203
|
// For Yandex Ads multi-provider support
|
|
200
|
-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v4.4.
|
|
204
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v4.4.2'
|
|
201
205
|
```
|
|
202
206
|
|
|
203
207
|
**Step 3:** Ensure your app's `compileSdk` is **37 or higher** (required transitively as of 4.2.0).
|
|
@@ -207,7 +211,7 @@ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v4.4.0'
|
|
|
207
211
|
## Features
|
|
208
212
|
|
|
209
213
|
### NativeTemplateView (v2.6.0+)
|
|
210
|
-
- **
|
|
214
|
+
- **38 Template Styles**: card_modern, material3, app_store, social_feed, gradient_card, pill_banner, medium_horizontal, flexible, icon_left, top_icon_media, spotlight, plus the video_* and flat_* families
|
|
211
215
|
- **XML & Programmatic**: Set templates via `app:adTemplate` or `setTemplate()`
|
|
212
216
|
- **Material 3 Theming**: Automatic dark/light mode support
|
|
213
217
|
- **AdChoices Control**: Configure placement position (v2.9.0+)
|
|
@@ -771,6 +775,8 @@ Register it **before** `initBilling`. Products land in `unfetched` when the id i
|
|
|
771
775
|
- [Multi-Provider Waterfall](docs/MULTI_PROVIDER_WATERFALL.md)
|
|
772
776
|
- [Yandex Integration](docs/YANDEX_INTEGRATION.md)
|
|
773
777
|
- [Billing Integration Guide](docs/APP_PURCHASE_GUIDE.md)
|
|
778
|
+
- [Release Notes v4.4.2](docs/release-notes/RELEASE_NOTES_v4.4.2.md)
|
|
779
|
+
- [Release Notes v4.4.1](docs/release-notes/RELEASE_NOTES_v4.4.1.md)
|
|
774
780
|
- [Release Notes v4.4.0](docs/release-notes/RELEASE_NOTES_v4.4.0.md)
|
|
775
781
|
- [Release Notes v4.3.5](docs/release-notes/RELEASE_NOTES_v4.3.5.md)
|
|
776
782
|
- [Release Notes v4.3.4](docs/release-notes/RELEASE_NOTES_v4.3.4.md)
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
# Ad Loading Strategies Guide
|
|
2
2
|
|
|
3
|
-
**New in v2.6.0** - AdManageKit supports
|
|
3
|
+
**New in v2.6.0** - AdManageKit supports four different ad loading strategies to fit different use cases in your app.
|
|
4
4
|
|
|
5
5
|
## Strategy Availability
|
|
6
6
|
|
|
7
7
|
| Strategy | Interstitial | App Open | Native |
|
|
8
8
|
|----------|-------------|----------|--------|
|
|
9
9
|
| ON_DEMAND | ✅ | ✅ | ✅ |
|
|
10
|
-
| ONLY_CACHE | ✅ | ✅ |
|
|
10
|
+
| ONLY_CACHE | ✅ | ✅ | ✅ |
|
|
11
11
|
| HYBRID | ✅ | ✅ | ✅ |
|
|
12
|
+
| FRESH_WITH_CACHE_FALLBACK | ✅ | ✅ | ✅ |
|
|
12
13
|
|
|
13
|
-
> **Note:**
|
|
14
|
+
> **Note:** all four strategies work for every ad type. `ONLY_CACHE` behaves differently
|
|
15
|
+
> on native ads: instead of blocking with a dialog, it shows a cached ad if one is ready
|
|
16
|
+
> and otherwise hides the container (see [Native Ads Behavior](#native-ads-behavior)).
|
|
14
17
|
|
|
15
18
|
## Strategy Types
|
|
16
19
|
|
|
@@ -82,6 +85,26 @@ User triggers ad → Check cache →
|
|
|
82
85
|
If timeout: Skip ad → Continue
|
|
83
86
|
```
|
|
84
87
|
|
|
88
|
+
### 4. FRESH_WITH_CACHE_FALLBACK
|
|
89
|
+
**Fetch fresh first, fall back to cache if the load fails**
|
|
90
|
+
|
|
91
|
+
- ✅ Freshest creative whenever the network cooperates
|
|
92
|
+
- ✅ A warm cache covers the failure case
|
|
93
|
+
- ✅ Successful loads are cached for later requests
|
|
94
|
+
- ⚠️ Slower than cache-first when the fetch is slow
|
|
95
|
+
|
|
96
|
+
**Best for:**
|
|
97
|
+
- RecyclerView / feed placements where each item should try for a fresh ad
|
|
98
|
+
- Placements where creative freshness matters more than instant fill
|
|
99
|
+
|
|
100
|
+
**How it works:**
|
|
101
|
+
```
|
|
102
|
+
Load ad → Fetch fresh →
|
|
103
|
+
If loaded: Show ad (and cache it for next time)
|
|
104
|
+
If failed: Fall back to cached ad if one exists
|
|
105
|
+
If no cache: Skip / hide container
|
|
106
|
+
```
|
|
107
|
+
|
|
85
108
|
---
|
|
86
109
|
|
|
87
110
|
## Native Ads Behavior
|
|
@@ -118,6 +141,14 @@ Load native ad → Check cache →
|
|
|
118
141
|
❌ On failure → Hide container
|
|
119
142
|
```
|
|
120
143
|
|
|
144
|
+
**FRESH_WITH_CACHE_FALLBACK:**
|
|
145
|
+
```
|
|
146
|
+
Load native ad → Show shimmer → Fetch fresh ad →
|
|
147
|
+
✅ Show ad when loaded → Hide shimmer (ad is cached for next time)
|
|
148
|
+
❌ On failure → Show cached ad if one exists
|
|
149
|
+
❌ No cache → Hide container
|
|
150
|
+
```
|
|
151
|
+
|
|
121
152
|
### Usage Example
|
|
122
153
|
|
|
123
154
|
```kotlin
|
|
@@ -7,14 +7,14 @@ AdManageKit provides native Jetpack Compose components for all ad types with ful
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```groovy
|
|
10
|
-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:
|
|
10
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v4.4.2'
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Available Components
|
|
14
14
|
|
|
15
15
|
| Component | Description |
|
|
16
16
|
|-----------|-------------|
|
|
17
|
-
| `NativeTemplateCompose` | Unified native ad with
|
|
17
|
+
| `NativeTemplateCompose` | Unified native ad with 38 templates, or a fully custom layout (NEW in 2.6.0) |
|
|
18
18
|
| `BannerAdCompose` | Banner ad with lifecycle management |
|
|
19
19
|
| `NativeBannerSmallCompose` | Small native banner (80dp) |
|
|
20
20
|
| `NativeBannerMediumCompose` | Medium native banner (120dp) |
|
|
@@ -29,7 +29,7 @@ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v2.6.0'
|
|
|
29
29
|
|
|
30
30
|
## NativeTemplateCompose (NEW in 2.6.0)
|
|
31
31
|
|
|
32
|
-
A Compose wrapper for `NativeTemplateView` supporting all
|
|
32
|
+
A Compose wrapper for `NativeTemplateView` supporting all 38 template styles.
|
|
33
33
|
|
|
34
34
|
### Basic Usage
|
|
35
35
|
|
|
@@ -86,6 +86,18 @@ NativeAdTemplate.GRID_ITEM // Compact grid item
|
|
|
86
86
|
NativeAdTemplate.TOP_ICON_MEDIA // Top icon, media center
|
|
87
87
|
NativeAdTemplate.ICON_LEFT // Left icon column layout
|
|
88
88
|
|
|
89
|
+
// Flat-design family (10 styles, v3.6.0+)
|
|
90
|
+
NativeAdTemplate.FLAT_INLINE_ROW
|
|
91
|
+
NativeAdTemplate.FLAT_CARD_RATING
|
|
92
|
+
NativeAdTemplate.FLAT_MEDIA_TOP
|
|
93
|
+
NativeAdTemplate.FLAT_TEXT_MINIMAL
|
|
94
|
+
NativeAdTemplate.FLAT_COMPACT_PILL
|
|
95
|
+
NativeAdTemplate.FLAT_CAROUSEL
|
|
96
|
+
NativeAdTemplate.FLAT_BANNER
|
|
97
|
+
NativeAdTemplate.FLAT_FEATURE_LIST
|
|
98
|
+
NativeAdTemplate.FLAT_SPONSORED_STORY
|
|
99
|
+
NativeAdTemplate.FLAT_FOOTER_SLIM
|
|
100
|
+
|
|
89
101
|
// Video Templates
|
|
90
102
|
NativeAdTemplate.VIDEO_SMALL // 120x120dp
|
|
91
103
|
NativeAdTemplate.VIDEO_MEDIUM // 180dp height
|
|
@@ -111,13 +123,75 @@ NativeVideoLargeCompose(adUnitId = adUnitId)
|
|
|
111
123
|
NativeVideoSquareCompose(adUnitId = adUnitId)
|
|
112
124
|
```
|
|
113
125
|
|
|
126
|
+
### Custom Templates & Shimmer (v4.3.0+)
|
|
127
|
+
|
|
128
|
+
If none of the 38 built-in templates fit your design, pass your own layout instead of a `template`:
|
|
129
|
+
|
|
130
|
+
```kotlin
|
|
131
|
+
NativeTemplateCompose(
|
|
132
|
+
adUnitId = "ca-app-pub-xxx/yyy",
|
|
133
|
+
customLayoutResId = R.layout.my_native_ad,
|
|
134
|
+
customShimmerResId = R.layout.my_native_ad_shimmer, // strongly recommended, see below
|
|
135
|
+
customSizeHint = NativeAdSize.MEDIUM
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`customLayoutResId` takes precedence over `template` when both are set.
|
|
140
|
+
|
|
141
|
+
**Layout requirements.** The root must be (or inflate as) a Next-Gen SDK `NativeAdView`, and the asset views must reuse the standard ids so the library can bind them:
|
|
142
|
+
|
|
143
|
+
`ad_headline`, `ad_body`, `ad_call_to_action`, `ad_app_icon`, `ad_advertiser`, `ad_media`, `ad_stars`, `ad_choices_view`
|
|
144
|
+
|
|
145
|
+
Any id you omit is simply not populated — but the headline and call-to-action should always be present, and the call-to-action must remain fully visible and tappable or the native ad validator flags a policy violation.
|
|
146
|
+
|
|
147
|
+
#### The shimmer parameter is optional, but omitting it rarely does what you want
|
|
148
|
+
|
|
149
|
+
`customShimmerResId` is not required. If you leave it out, the shimmer falls back to **the shimmer belonging to `template`** — which defaults to `CARD_MODERN`:
|
|
150
|
+
|
|
151
|
+
```kotlin
|
|
152
|
+
// NativeTemplateView.kt
|
|
153
|
+
get() = customShimmerResId ?: currentTemplate.shimmerResId
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
So a custom layout with no custom shimmer still shows a placeholder — just one shaped like a completely different ad. The usual symptom is a placeholder that visibly jumps to a different size when the real ad arrives.
|
|
157
|
+
|
|
158
|
+
Two ways to get this right:
|
|
159
|
+
|
|
160
|
+
```kotlin
|
|
161
|
+
// 1. Supply a shimmer matching your layout (recommended)
|
|
162
|
+
NativeTemplateCompose(
|
|
163
|
+
adUnitId = adUnitId,
|
|
164
|
+
customLayoutResId = R.layout.my_native_ad,
|
|
165
|
+
customShimmerResId = R.layout.my_native_ad_shimmer
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
// 2. No custom shimmer: set `template` to whichever built-in most resembles your
|
|
169
|
+
// layout, so the fallback is at least the right shape. Every template carries
|
|
170
|
+
// its own shimmer (NativeAdTemplate.shimmerResId), and that is what gets used.
|
|
171
|
+
NativeTemplateCompose(
|
|
172
|
+
adUnitId = adUnitId,
|
|
173
|
+
template = NativeAdTemplate.MEDIUM_HORIZONTAL, // example: its shimmer is the fallback
|
|
174
|
+
customLayoutResId = R.layout.my_native_ad
|
|
175
|
+
)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Passing `0` for `customShimmerResId` is treated the same as omitting it.
|
|
179
|
+
|
|
180
|
+
**`customSizeHint`** classifies the ad for the cache and gives the Yandex waterfall a fallback size, since a custom layout has no built-in size bucket. Values are `NativeAdSize.SMALL` (icon + title + CTA), `MEDIUM` (adds body), `LARGE` (adds media). Pick whichever matches your layout's content; it does not affect how your layout is measured.
|
|
181
|
+
|
|
182
|
+
The same feature in XML and code is documented in the [NativeTemplateView guide](NATIVE_TEMPLATE_VIEW.md#custom-templates).
|
|
183
|
+
|
|
114
184
|
---
|
|
115
185
|
|
|
116
186
|
## Loading Strategies (NEW in 2.6.0)
|
|
117
187
|
|
|
118
|
-
Native ad Compose components
|
|
188
|
+
Native ad Compose components accept a `loadingStrategy` parameter. All four strategies
|
|
189
|
+
are supported for native ads — `ON_DEMAND`, `ONLY_CACHE`, `HYBRID` (default) and
|
|
190
|
+
`FRESH_WITH_CACHE_FALLBACK`.
|
|
119
191
|
|
|
120
|
-
>
|
|
192
|
+
> **`ONLY_CACHE` on native ads** shows a cached ad if one is ready and otherwise hides
|
|
193
|
+
> the container, rather than blocking the UI with a dialog the way interstitial and app
|
|
194
|
+
> open ads do. See the [Ad Loading Strategies guide](AD_LOADING_STRATEGIES.md) for the full matrix.
|
|
121
195
|
|
|
122
196
|
```kotlin
|
|
123
197
|
// ON_DEMAND - Always fetch fresh ad
|
|
@@ -202,7 +202,10 @@ class MainActivity : AppCompatActivity() {
|
|
|
202
202
|
// Configure ONLY_CACHE strategy
|
|
203
203
|
AdManageKitConfig.apply {
|
|
204
204
|
interstitialLoadingStrategy = AdLoadingStrategy.ONLY_CACHE
|
|
205
|
-
|
|
205
|
+
// Native ads support ONLY_CACHE too - there is no auto-conversion. Set it
|
|
206
|
+
// explicitly if you want cache-or-nothing; HYBRID here is a deliberate choice
|
|
207
|
+
// to still fetch when the cache misses.
|
|
208
|
+
nativeLoadingStrategy = AdLoadingStrategy.HYBRID
|
|
206
209
|
enableSmartPreloading = true
|
|
207
210
|
}
|
|
208
211
|
|
|
@@ -387,7 +390,7 @@ AdManageKitConfig.apply {
|
|
|
387
390
|
## Testing
|
|
388
391
|
|
|
389
392
|
See `LoadingStrategyTestActivity.kt` for comprehensive testing of:
|
|
390
|
-
- All
|
|
393
|
+
- All four loading strategies
|
|
391
394
|
- Preload functionality
|
|
392
395
|
- Cache management
|
|
393
396
|
- Real-time cache status monitoring
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# NativeTemplateView Guide
|
|
2
2
|
|
|
3
|
-
**New in v2.6.0** - A unified native ad component supporting
|
|
3
|
+
**New in v2.6.0** - A unified native ad component supporting 38 different template styles with Material 3 theming and video support.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
`NativeTemplateView` is a single, flexible component that replaces the need for multiple native ad views. It supports
|
|
7
|
+
`NativeTemplateView` is a single, flexible component that replaces the need for multiple native ad views. It supports 38 different visual templates that can be set via XML attributes or programmatically.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
|
-
- **
|
|
11
|
+
- **38 Template Styles**: From minimal to icon-left, compact to fullscreen video
|
|
12
12
|
- **Material 3 Theming**: Automatic dark/light mode support
|
|
13
13
|
- **Video Support**: All templates support video ads (120dp+ MediaView)
|
|
14
14
|
- **Shimmer Loading**: Beautiful loading animation
|
|
@@ -273,6 +273,10 @@ NativeTemplateCompose(
|
|
|
273
273
|
)
|
|
274
274
|
```
|
|
275
275
|
|
|
276
|
+
`customLayoutResId` takes precedence over `template`, mirroring `app:customAdLayout` in XML.
|
|
277
|
+
|
|
278
|
+
`customShimmerResId` is optional, but if omitted the shimmer falls back to the one belonging to `template` (default `CARD_MODERN`) — so a custom layout shows a placeholder shaped like a different ad. Either supply a matching shimmer or set `template` to the closest built-in. See [Custom Templates & Shimmer](COMPOSE_INTEGRATION.md#custom-templates--shimmer-v430) in the Compose guide for the full explanation.
|
|
279
|
+
|
|
276
280
|
## Jetpack Compose Usage
|
|
277
281
|
|
|
278
282
|
NativeTemplateView is also available as a Compose component via the `ad-manage-kit-compose` module.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Release Notes — v4.4.1
|
|
2
|
+
|
|
3
|
+
**Release Date:** 2026-07-29
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
v4.4.1 is a patch release with one dependency update and a set of documentation-delivery repairs. **No AdManageKit API changed** — no signature was added, removed, or altered.
|
|
8
|
+
|
|
9
|
+
The dependency update moves the **Google Mobile Ads Next-Gen SDK from 1.2.1 to 1.3.0**. Since the SDK is an `api` dependency, this is what your app resolves transitively.
|
|
10
|
+
|
|
11
|
+
The rest of the release fixes everything *around* the library — the API documentation site and the MCP documentation server that AI coding assistants read. Three delivery channels had silently gone stale, each in a way that made the library look older than it is:
|
|
12
|
+
|
|
13
|
+
- **The API docs site** had not regenerated since the Dokka 2.x upgrade. Every release since then, including 4.4.0, failed its documentation workflow, so [the published KDoc](https://i2hammad.github.io/AdManageKit/) never gained any 4.x types.
|
|
14
|
+
- **The MCP server on npm** was stuck at 1.0.0 (published 2026-01-26). Assistants consuming it were being told about a pre-Next-Gen-SDK, pre-Billing-9 library.
|
|
15
|
+
- **The MCP server's own tool schemas** hardcoded which versions and classes existed, so even a fresh publish would have rejected `4.4.0` as an unknown version and reported February 2026 as the latest release.
|
|
16
|
+
|
|
17
|
+
All modules are bumped to **4.4.1**. The MCP server is published separately as **1.2.0**.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Changed
|
|
22
|
+
|
|
23
|
+
### Google Mobile Ads Next-Gen SDK 1.2.1 → 1.3.0
|
|
24
|
+
|
|
25
|
+
`gradle/libs.versions.toml` moves `adsMobileSdk` to **1.3.0**. AdManageKit exposes the SDK as an `api` dependency, so apps on 4.4.1 resolve 1.3.0 transitively unless they pin it themselves.
|
|
26
|
+
|
|
27
|
+
No AdManageKit code needed to change for this — the library was already on Next-Gen SDK patterns throughout (explicit `MobileAds.initialize()`, main-thread marshalling of background callbacks, Next-Gen `AdKit*` type aliases). `assembleDebug` and the full unit test suite (**168 tests, 0 failures**, forced re-run) pass against 1.3.0.
|
|
28
|
+
|
|
29
|
+
Consult [Google's Next-Gen SDK release notes](https://developers.google.com/admob/android/migration) for what 1.3.0 changes on the SDK side. If your app references the SDK's own types directly (rather than only AdManageKit's `AdKitError` / `AdKitLoadError` / `AdKitValue` aliases), review those call sites — but nothing in AdManageKit's surface requires action.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Fixed
|
|
34
|
+
|
|
35
|
+
### API documentation generation (Dokka)
|
|
36
|
+
|
|
37
|
+
The Deploy API Documentation workflow failed on every release since the Dokka 2.x upgrade. On an Android module, Dokka registers a source set per variant (`debug`, `release`, plus test/androidTest/testFixtures) *and* a generic `main`. Since `main`, `debug` and `release` all cover `src/main`, this tripped Dokka's duplicate-source-root check:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
Pre-generation validity check failed: Source sets 'androidJvm' and 'release'
|
|
41
|
+
have the common source roots: .../src/main/kotlin, .../src/main/java
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
All five modules were affected; CI only reported two because the build stopped early. Dokka is now configured to document the **release variant only** — matching what each module actually publishes via `components["release"]` — and to suppress the rest. That also keeps test and androidTest sources out of the public API docs, which they were never meant to be eligible for.
|
|
45
|
+
|
|
46
|
+
The configuration lives in the root build rather than being repeated across five modules, so any new module is covered automatically.
|
|
47
|
+
|
|
48
|
+
`dokkaGeneratePublicationHtml` now emits **1244 pages** across all five modules, including the 4.4.0 billing types (`BillingPeriod`, `OneTimeOfferInfo`, `ProductDetailsListener`, `InAppMessageListener`) and the new `AppPurchase` members. No test-source pages are emitted.
|
|
49
|
+
|
|
50
|
+
### MCP server npm publishing
|
|
51
|
+
|
|
52
|
+
The `publish-npm` job ran checkout, setup-node, download-artifact and `npm publish` with **no dependency install**. Because `package.json` declares `"prepublishOnly": "npm run build"`, `npm publish` re-ran `tsc` in a job with no `node_modules` and died with `TS2307 Cannot find module` on every import. The build job's uploaded artifact could not help — `prepublishOnly` rebuilds from source regardless.
|
|
53
|
+
|
|
54
|
+
- `npm ci` added to `publish-npm` so `prepublishOnly` can build. The redundant `download-artifact` step is dropped; the build job stays as a verification gate.
|
|
55
|
+
- **`zod` declared as a direct dependency.** It is imported by `tools/code-generation.ts` and `tools/documentation.ts` but appeared in neither `dependencies` nor `devDependencies`, resolving only because npm hoisted it out of `@modelcontextprotocol/sdk`. An SDK release nesting zod differently would have broken the build with no source change. The range mirrors the SDK's own (`^3.25 || ^4.0`) so npm can dedupe.
|
|
56
|
+
|
|
57
|
+
### MCP tool schemas drifting from bundled docs
|
|
58
|
+
|
|
59
|
+
Publishing current documentation was not enough — the code gating access to it was stale:
|
|
60
|
+
|
|
61
|
+
- `get_release_notes` accepted only 17 versions while **38** release-notes files were bundled. Every version from 3.4.0 onward, including 4.4.0, was rejected at schema validation, and `"latest"` was hardcoded to 3.3.8.
|
|
62
|
+
- `get_migration_guide` omitted **4.2.0**, the Next-Gen SDK migration — the one most consumers need.
|
|
63
|
+
- `get_api_reference` listed 16 classes against 34 documented sections, hiding `OfferInfo`, `BillingPeriod`, `PurchaseResult` and others.
|
|
64
|
+
- `TOPIC_MAP` had no entry for the `Subscription-Offers` wiki page.
|
|
65
|
+
|
|
66
|
+
These lists are now **derived at startup from the bundled content**, so shipping a doc is sufficient to make it reachable: `discoverReleaseVersions()` scans `docs/release-notes/` (sorted newest-first by numeric version, with `"latest"` resolving to the newest file actually present), `discoverMigrationVersions()` scans the README for `### Migrating to X`, and `discoverApiClassNames()` reuses the `API_REFERENCE.md` section parser. `list_documentation` renders from those and appends any `TOPIC_MAP` key the hand-written grouping missed, so a registered topic can never be undiscoverable. The hardcoded `RELEASE_VERSIONS`, `MIGRATION_VERSIONS` and `API_CLASS_NAMES` constants are gone, leaving nothing to drift.
|
|
67
|
+
|
|
68
|
+
`z.enum()` requires a non-empty tuple, so discovery degrades to a plain string when it finds nothing, rather than throwing at registration and taking the server down.
|
|
69
|
+
|
|
70
|
+
### MCP code-generation templates emitting obsolete APIs
|
|
71
|
+
|
|
72
|
+
The generator templates are hardcoded TypeScript, so unlike the bundled docs they gained nothing from republishing. They emitted code against APIs two major versions old — including one case that does not work at all.
|
|
73
|
+
|
|
74
|
+
- **`generate_config` emitted no `MobileAds.initialize()` call.** Since v4.2.0 AdManageKit runs on the Next-Gen SDK, which removed the legacy SDK's silent lazy-init, so the generated `Application` class produced an app that never loads ads. Both the Kotlin and Java templates now initialize explicitly, off the main thread (`initialize()` blocks and can ANR), read the application id from the manifest, and construct `AppOpenManager` only after initialization returns — otherwise its lifecycle observer can fire a load the SDK rejects as "not initialized". This mirrors the sample app's `MyApplication.kt`.
|
|
75
|
+
- **Billing `setup`** now emits `setDebugMode(BuildConfig.DEBUG)` (a library AAR always has `BuildConfig.DEBUG = false`, so the host app must inject its build state), obfuscated account ids, and the `ProductDetailsListener` for diagnosing an empty paywall.
|
|
76
|
+
- **Billing `subscribe`** showed only `subscribe(activity, id)`, which resolves the offer itself and on a multi-offer product can charge for a plan the user did not pick. It now shows the offer-explicit form and explains why.
|
|
77
|
+
- **New `offers` scenario** — multi-offer paywalls: enumerating offers, buying a specific one, savings badges, per-month normalization, trial eligibility, one-time product offers.
|
|
78
|
+
- **New `account_hold` scenario** — client-side hold detection, Play's payment recovery flow, pending plan changes.
|
|
79
|
+
- `subscription_management` and `subscribe` now handle `SubscriptionState.ON_HOLD`, which 4.4.0 made reachable.
|
|
80
|
+
- The **Java template** was a stub covering only `setup` and `purchase`; it now mirrors the Kotlin coverage.
|
|
81
|
+
- **`FRESH_WITH_CACHE_FALLBACK` was missing from every loading-strategy enum**, and native ads were restricted to two of the four values. All four fields now use a shared `AD_LOADING_STRATEGIES` constant mirroring `AdLoadingStrategy.kt`.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Verification
|
|
86
|
+
|
|
87
|
+
- `assembleDebug` succeeds and `testDebugUnitTest --rerun-tasks` passes **168 tests with 0 failures** against Next-Gen SDK 1.3.0 (forced re-run, not an up-to-date cache hit).
|
|
88
|
+
- `dokkaGeneratePublicationHtml` succeeds across all five modules (1244 pages, no test-source pages).
|
|
89
|
+
- `npm publish --dry-run` exercises the previously failing `prepublishOnly` path: builds clean, produces a 103-file tarball carrying 58 docs and 19 wiki pages.
|
|
90
|
+
- A live MCP stdio session against real docs resolves 38 release versions (4.4.0 newest, `"latest"` → v4.4.0), 5 migration guides including 4.2.0, 33 API classes including `OfferInfo` and `BillingPeriod`, and 24 topics including `subscription-offers`.
|
|
91
|
+
- Against a docs-less root, the server still starts, schemas degrade with no enum, and handlers return their normal "not found" messages.
|
|
92
|
+
- Every symbol emitted by the refreshed templates was checked against library source: 25 `AppPurchase` methods, 15 `OfferInfo`/`OneTimeOfferInfo` properties, `SubscriptionState.ON_HOLD`, `BillingPeriod.formatOf`, and the `InAppMessageListener` / `ProductDetailsListener` method names all resolve. All nine billing scenarios generate in both languages.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Upgrading
|
|
97
|
+
|
|
98
|
+
Drop-in from 4.4.0 — no AdManageKit API changed, so no source edits are required.
|
|
99
|
+
|
|
100
|
+
The one thing to be aware of is the transitive **Next-Gen SDK bump to 1.3.0**. If your app pins `com.google.android.libraries.ads.mobile.sdk` itself, either raise your pin to 1.3.0 or expect Gradle's conflict resolution to pick the higher version anyway. If your app calls the SDK's own types directly — rather than only AdManageKit's `AdKitError` / `AdKitLoadError` / `AdKitValue` aliases — give those call sites a compile pass.
|
|
101
|
+
|
|
102
|
+
```groovy
|
|
103
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v4.4.1'
|
|
104
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v4.4.1'
|
|
105
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.1'
|
|
106
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v4.4.1'
|
|
107
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v4.4.1'
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
For the MCP server, `npx -y admanagekit-mcp-server` now resolves to 1.2.0 or newer.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Release Notes — v4.4.2
|
|
2
|
+
|
|
3
|
+
**Release Date:** 2026-07-29
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
v4.4.2 is a bug-fix release. No API was added, removed or changed — but several of these are user-visible, and two affect revenue directly:
|
|
8
|
+
|
|
9
|
+
- **Rewarded ads could crash the app.** Every rewarded show-path callback, including `onRewardEarned`, was delivered on a background thread.
|
|
10
|
+
- **A completed purchase could fail to disable ads**, leaving a paying customer looking at ads until the next app launch.
|
|
11
|
+
- **Blank gaps** where a banner or native ad should have collapsed — for premium users and after a failed load, in both XML and Compose.
|
|
12
|
+
- **App open ads could appear over excluded screens**, including flows protected by `disableAppOpenAdsTemporarily()`.
|
|
13
|
+
|
|
14
|
+
Upgrading is a drop-in from 4.4.x. Two behavior changes are called out under [Behavior changes](#behavior-changes).
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Fixed
|
|
19
|
+
|
|
20
|
+
### Rewarded ads: callbacks delivered off the main thread
|
|
21
|
+
|
|
22
|
+
`RewardedAdManager` attached the Next-Gen SDK's event callbacks directly, with no marshalling anywhere on the direct (non-waterfall) path. `onAdDismissed`, `onAdShowed`, `onAdFailedToShow`, `onAdClicked` and — most importantly — the `onRewardEarned` reward listener all ran on a background thread.
|
|
23
|
+
|
|
24
|
+
`onRewardEarned` and `onAdDismissed` are **abstract** on `RewardedAdCallback`: every consumer implements them, and they are exactly where apps grant the reward and update the UI. The result was a `CalledFromWrongThreadException` or a silently corrupted UI update on every rewarded flow that touched a view.
|
|
25
|
+
|
|
26
|
+
All load, show and reward callbacks now post to the main thread. The waterfall path was already safe via its providers, but is now marshalled explicitly too, so the guarantee holds for third-party providers registered through `AdProviderConfig` rather than depending on their behavior.
|
|
27
|
+
|
|
28
|
+
> The v4.3.1 threading fix covered interstitial `onNextAction`, the banner/native waterfall chains and `NativeTemplateView` — rewarded was missed.
|
|
29
|
+
|
|
30
|
+
### A completed purchase could fail to disable ads
|
|
31
|
+
|
|
32
|
+
`AppPurchase.handlePurchase(Purchase)` wrapped every entitlement mutation in `if (productDetailsMap.get(productId) != null)`. That map is populated **only** by a successful `queryProductDetailsAsync`, and Play declines product ids routinely — which is why `getUnfetchedProducts()` exists.
|
|
33
|
+
|
|
34
|
+
When details were missing, the purchase was acknowledged, `onProductPurchased` fired, and the entitlement was silently dropped: `isPurchased()` kept returning `false` and **ads kept serving to a paying customer**. Nothing masked it, either — `verifyPurchased()` runs only on billing-service connect, so the state stayed wrong until the next app launch.
|
|
35
|
+
|
|
36
|
+
Product type is now resolved from the configured `subProductIdList` / `inAppProductIdList` first — matching what `verifyPurchased()` already did — then from `productDetailsMap`, and finally from the purchase's own `isAutoRenewing()` flag. Granting on a reasonable inference beats billing someone and leaving the ads on.
|
|
37
|
+
|
|
38
|
+
### Blank gaps where an ad should have collapsed
|
|
39
|
+
|
|
40
|
+
Two independent causes, both producing the same symptom.
|
|
41
|
+
|
|
42
|
+
**In XML/View usage:** `NativeBannerSmall`, `NativeBannerMedium` and `NativeLarge` hid only their shimmer for premium users and left the root visible, leaving an empty view with its padding and background. `NativeBannerMedium` never collapsed on load failure at all, and `NativeBannerSmall` only did so on the strategy path, not the direct AdMob one. All three now collapse the root, and reset visibility at load start so a later successful load isn't left invisible.
|
|
43
|
+
|
|
44
|
+
**In Compose:** every wrapper claimed its height with a Compose modifier while the "hide me" logic lived in the View's `visibility`. Setting `visibility = GONE` cannot shrink the Compose node, so the ad vanished and the gap remained. Each `update` block then forced the view back to `VISIBLE`, undoing the hide the library had just performed — and for banners that ran through `showAd()`, which **restarts the shimmer** on an empty slot.
|
|
45
|
+
|
|
46
|
+
Height is now driven by slot state rather than view visibility (`LOADING` and `SHOWN` reserve the ad's height so a fill doesn't shift the layout; `HIDDEN` occupies nothing), and no `update` block forces visibility unconditionally. `ProgrammaticNativeAdCompose`'s error branch rendered empty content inside a still-reserved 80–300dp box; that box now collapses.
|
|
47
|
+
|
|
48
|
+
**All five Compose ad composables now gate on `rememberPurchaseStatus()`**, so a premium user issues no ad request and reserves no space. That helper already existed and was reactive — nothing internal used it.
|
|
49
|
+
|
|
50
|
+
### Compose silently loaded nothing on a wrapped context
|
|
51
|
+
|
|
52
|
+
Every Compose ad entry point gated on `context is ComponentActivity` — 12 sites, with no `ContextWrapper` unwrapping and, at ten of them, no `else` branch.
|
|
53
|
+
|
|
54
|
+
`LocalContext.current` is the Activity in the common `setContent { }` case, but it is a `ContextThemeWrapper` inside `Dialog` / `ModalBottomSheet` composables, under per-screen theme wrappers, and in some `AndroidView`-nested compositions. There, the composable loaded nothing, fired no failure callback, and rendered an empty box forever with no diagnostics.
|
|
55
|
+
|
|
56
|
+
New `Context.findComponentActivity()` unwraps the wrapper chain (mirroring `NativeBannerSmall.findHostActivity()`), and every site now reports a failure instead of failing silently.
|
|
57
|
+
|
|
58
|
+
### App open ads could appear over excluded screens
|
|
59
|
+
|
|
60
|
+
When an ad finished loading while the app was backgrounded, `onStart` routed it through a pending-ad path that checked purchase state, interstitial conflicts and activity validity — but **never `isActivityExcluded(...)` and never `skipNextAd`**.
|
|
61
|
+
|
|
62
|
+
An app that called `disableAppOpenAdsTemporarily()` before a payment flow, or excluded its splash/PIN activity, could still get a full-screen ad there if the user backgrounded the app mid-fetch and returned. Both gates are now honored; the loaded ad stays cached for the next legitimate opportunity.
|
|
63
|
+
|
|
64
|
+
### Retries could strand the caller waiting on them
|
|
65
|
+
|
|
66
|
+
`AdRetryManager` keys retries by ad unit id, so scheduling for an id that already has one **replaces** it. `NativeAdIntegrationManager` deliberately withholds the failure callback when it schedules a retry, expecting that retry to deliver the result.
|
|
67
|
+
|
|
68
|
+
Because the screen-specific key resolves to the *base* ad unit id, two native views sharing one ad unit (a `SMALL` and a `MEDIUM` on the same screen) collide — and the evicted retry's owner was never told anything. Its view held its shimmer forever.
|
|
69
|
+
|
|
70
|
+
`scheduleRetry` now accepts an `onDropped` callback, invoked when a retry is replaced, cancelled, or refused because retries are disabled/exhausted. It is delivered via `Handler.post` so a handler that reacts by scheduling another retry cannot re-enter `scheduleRetry` mid-mutation. A non-atomic get-then-remove in `cancelRetry` was fixed alongside it.
|
|
71
|
+
|
|
72
|
+
### Duplicate callbacks and duplicate ad shows
|
|
73
|
+
|
|
74
|
+
`AdManager`'s force-show and splash flows guarded their outcomes with plain captured `var`s, written from both the SDK's background thread and the main-thread timeout. Neither side could see the other's write reliably, so a timeout landing alongside a load could deliver `onNextAction()` twice — breaking the once-per-flow contract callers gate navigation on — or attempt two shows.
|
|
75
|
+
|
|
76
|
+
Each flow now uses a single `AtomicBoolean` latch shared across load-success, load-failure and timeout, so exactly one outcome wins.
|
|
77
|
+
|
|
78
|
+
`AdManager.onAdShowedFullScreenContent` also invoked `callback.onAdShowed()` on the SDK thread while the dismiss and failure paths three lines away marshalled correctly; it now matches them.
|
|
79
|
+
|
|
80
|
+
### Leaks
|
|
81
|
+
|
|
82
|
+
- **`BannerAdView` never stopped auto-refresh on detach.** A banner inside a `RecyclerView`, `ViewPager` or a fragment whose view is destroyed kept requesting ads on schedule — indefinitely, since each refresh schedules the next — and the pending `Runnable` retained the Activity. Host `ON_DESTROY` only covers the case where the whole Activity goes away. Refresh now stops on detach and resumes on re-attach, and the lifecycle observer is unregistered on cleanup.
|
|
83
|
+
- **`RewardedAdManager`'s retry closures captured the Activity** passed to `showAd()`, parked on the main `Handler` for up to `maxRetryDelay` off a process-lifetime singleton. They now use the application context, as `AdManager` already did at three sites.
|
|
84
|
+
|
|
85
|
+
### Also hardened
|
|
86
|
+
|
|
87
|
+
- `AdManager` no longer opens its loading dialog over a finishing or destroyed activity. A `BadTokenException` there left `isFetchingWithDialog` stuck `true`, permanently short-circuiting every later force-show to `onNextAction()`.
|
|
88
|
+
- `showWaterfallWithWelcomeDialog` attaches to an in-flight load instead of starting a duplicate one, matching its non-waterfall twin.
|
|
89
|
+
- Restored / already-acknowledged purchases now reach the purchase history listener; that branch previously skipped it.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Behavior changes
|
|
94
|
+
|
|
95
|
+
Both are deliberate, and both follow the library's existing account-hold stance.
|
|
96
|
+
|
|
97
|
+
1. **An account-hold subscription no longer disables ads.** `getSubscriptionState()` already returned `ON_HOLD` and `isSubscriptionActive()` already returned `false` for these users, but `isPurchased()` still counted them — so they got no premium features *and* no ads. If your app deliberately keeps serving on-hold users ad-free, check `isSuspended()` explicitly. Pair `hasSubscriptionOnHold()` with `showInAppMessages(...)` to prompt a payment fix.
|
|
98
|
+
|
|
99
|
+
2. **Premium users no longer reserve ad space in Compose.** Composables return before creating the view, so the slot occupies zero height rather than an empty block. If your layout relied on that reserved space, add it explicitly.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Verification
|
|
104
|
+
|
|
105
|
+
`assembleDebug` succeeds and `testDebugUnitTest --rerun-tasks` passes **173 tests, 0 failures** (168 existing plus 5 new `AdRetryManager` cases covering the `onDropped` contract, including a negative case proving a retry that runs does not also report dropped).
|
|
106
|
+
|
|
107
|
+
The layout fixes are **not** covered by automated tests — the Compose module has no test source set and the View-layer visibility changes need a real hierarchy. They were verified by reading the visibility paths end to end; confirming them on-device is recommended.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Upgrading
|
|
112
|
+
|
|
113
|
+
Drop-in from 4.4.x. No source changes required; review the two behavior changes above.
|
|
114
|
+
|
|
115
|
+
```groovy
|
|
116
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v4.4.2'
|
|
117
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v4.4.2'
|
|
118
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.2'
|
|
119
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v4.4.2'
|
|
120
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v4.4.2'
|
|
121
|
+
```
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# Ad Loading Strategies - AdManageKit
|
|
1
|
+
# Ad Loading Strategies - AdManageKit v4.4.2
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
AdManageKit v2.6.0+ introduces
|
|
5
|
+
AdManageKit v2.6.0+ introduces four loading strategies that control how ads are fetched and displayed. Choose the right strategy based on your app's needs for user experience vs. ad coverage.
|
|
6
6
|
|
|
7
7
|
## Strategy Types
|
|
8
8
|
|
|
@@ -63,15 +63,33 @@ AdManageKitConfig.interstitialLoadingStrategy = AdLoadingStrategy.HYBRID
|
|
|
63
63
|
- Default recommendation
|
|
64
64
|
- Balanced UX and coverage
|
|
65
65
|
|
|
66
|
+
### 4. FRESH_WITH_CACHE_FALLBACK
|
|
67
|
+
|
|
68
|
+
**Fetch fresh first, fall back to cache on failure**
|
|
69
|
+
|
|
70
|
+
```kotlin
|
|
71
|
+
AdManageKitConfig.nativeLoadingStrategy = AdLoadingStrategy.FRESH_WITH_CACHE_FALLBACK
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
| Pros | Cons |
|
|
75
|
+
|------|------|
|
|
76
|
+
| Freshest creative when the network cooperates | Slower than cache-first when the fetch is slow |
|
|
77
|
+
| Cache covers the failure case | Requires a warm cache to help |
|
|
78
|
+
| Successful loads are cached for later | - |
|
|
79
|
+
|
|
80
|
+
**Best for:**
|
|
81
|
+
- RecyclerView / feed scenarios where each item should try for a fresh ad
|
|
82
|
+
- Placements where creative freshness matters more than instant fill
|
|
83
|
+
|
|
66
84
|
## Strategy Availability
|
|
67
85
|
|
|
68
|
-
| Ad Type | ON_DEMAND | ONLY_CACHE | HYBRID |
|
|
69
|
-
|
|
70
|
-
| Interstitial | ✅ | ✅ | ✅ |
|
|
71
|
-
| App Open | ✅ | ✅ | ✅ |
|
|
72
|
-
| Native | ✅ |
|
|
86
|
+
| Ad Type | ON_DEMAND | ONLY_CACHE | HYBRID | FRESH_WITH_CACHE_FALLBACK |
|
|
87
|
+
|---------|-----------|------------|--------|---------------------------|
|
|
88
|
+
| Interstitial | ✅ | ✅ | ✅ | ✅ |
|
|
89
|
+
| App Open | ✅ | ✅ | ✅ | ✅ |
|
|
90
|
+
| Native | ✅ | ✅ | ✅ | ✅ |
|
|
73
91
|
|
|
74
|
-
> **Note:** Native ads use shimmer instead of dialog, so ONLY_CACHE hides container
|
|
92
|
+
> **Note:** Native ads use shimmer instead of a dialog, so `ONLY_CACHE` hides the container when nothing is cached rather than blocking the UI.
|
|
75
93
|
|
|
76
94
|
## Configuration
|
|
77
95
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# App Open Ads - AdManageKit v4.
|
|
1
|
+
# App Open Ads - AdManageKit v4.4.2
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
5
|
AdManageKit provides lifecycle-aware app open ad management through `AppOpenManager`. App open ads display when users launch or return to your app, with support for loading strategies, ad freshness enforcement, welcome dialogs, activity/screen exclusion, and the multi-provider waterfall.
|
|
6
6
|
|
|
7
|
-
**Library Version**: v4.
|
|
7
|
+
**Library Version**: v4.4.2
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
@@ -23,9 +23,9 @@ AdManageKit provides lifecycle-aware app open ad management through `AppOpenMana
|
|
|
23
23
|
|
|
24
24
|
```groovy
|
|
25
25
|
dependencies {
|
|
26
|
-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v4.
|
|
27
|
-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v4.
|
|
28
|
-
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.
|
|
26
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v4.4.2'
|
|
27
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v4.4.2'
|
|
28
|
+
implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.2'
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|