admanagekit-mcp-server 1.2.0 → 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 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.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).
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.2.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.
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 37 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).
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.0'
193
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v4.4.0'
194
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.0'
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.0'
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.0'
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
- - **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
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 three different ad loading strategies to fit different use cases in your app.
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:** `ONLY_CACHE` is only available for **Interstitial** and **App Open** ads. Native ads display inline with shimmer loading, so they always need to load content.
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:v2.6.0'
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 28 templates (NEW in 2.6.0) |
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 28 template styles.
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 support `loadingStrategy` parameter with `ON_DEMAND` and `HYBRID`:
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
- > **Note:** `ONLY_CACHE` is only available for Interstitial and App Open ads, not for native ads.
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
- nativeLoadingStrategy = AdLoadingStrategy.HYBRID // Auto-converted from ONLY_CACHE
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 three loading strategies
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 28 different template styles with Material 3 theming and video support.
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 28 different visual templates that can be set via XML attributes or programmatically.
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
- - **28 Template Styles**: From minimal to icon-left, compact to fullscreen video
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 v2.8.0
1
+ # Ad Loading Strategies - AdManageKit v4.4.2
2
2
 
3
3
  ## Overview
4
4
 
5
- AdManageKit v2.6.0+ introduces three 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.
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 if not cached.
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.3.5
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.3.5
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.3.5'
27
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v4.3.5'
28
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.3.5'
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
 
@@ -1,10 +1,10 @@
1
- # Banner Ads - AdManageKit v4.3.5
1
+ # Banner Ads - AdManageKit v4.4.2
2
2
 
3
3
  ## Overview
4
4
 
5
5
  AdManageKit provides `BannerAdView` for banner ad integration with every standard AdMob size, adaptive banners, collapsible banners, a size-aware loading shimmer, auto-refresh, and smart retry logic.
6
6
 
7
- **Library Version**: v4.3.5
7
+ **Library Version**: v4.4.2
8
8
 
9
9
  ## Features
10
10
 
@@ -23,8 +23,8 @@ AdManageKit provides `BannerAdView` for banner ad integration with every standar
23
23
 
24
24
  ```groovy
25
25
  dependencies {
26
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v4.3.5'
27
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.3.5'
26
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v4.4.2'
27
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.2'
28
28
  }
29
29
  ```
30
30
 
@@ -7,8 +7,8 @@ AdManageKit provides a comprehensive billing integration module (`admanagekit-bi
7
7
  ### 1. Add Dependency
8
8
 
9
9
  ```groovy
10
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v4.4.0'
11
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.0'
10
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v4.4.2'
11
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.2'
12
12
  ```
13
13
 
14
14
  ### 2. Define Products
@@ -1,4 +1,4 @@
1
- # Configuration - AdManageKit v2.8.0
1
+ # Configuration - AdManageKit v4.4.2
2
2
 
3
3
  ## Overview
4
4
 
@@ -32,6 +32,8 @@ class MyApp : Application() {
32
32
  }
33
33
  ```
34
34
 
35
+ > **Since 4.2.0 you must also call `MobileAds.initialize()` yourself**, before any ad request — the Next-Gen SDK removed the legacy SDK's silent lazy-init. `AdManageKitConfig` only configures this library's behavior; it does not initialize the ads SDK. See [[Home]] for the initialization pattern.
36
+
35
37
  ## All Configuration Options
36
38
 
37
39
  ### Performance Settings
@@ -138,6 +140,28 @@ AdManageKitConfig.apply {
138
140
  }
139
141
  ```
140
142
 
143
+ ### Native Ad Settings
144
+
145
+ | Setting | Description | Default |
146
+ |---------|-------------|---------|
147
+ | `nativeLoadingStrategy` | Loading strategy | HYBRID |
148
+ | `nativeCacheExpiry` | Cache expiry for native ads | 1 hour |
149
+ | `defaultNativeMediaAspect` | Media aspect-ratio hint sent with every native request (v4.3.3+) | ANY |
150
+ | `nativeVideoStartMuted` | Start video creatives muted (v4.3.3+) | true |
151
+ | `nativeVideoClickToExpand` | Allow click-to-expand on video creatives (v4.3.3+) | false |
152
+ | `nativeVideoCustomControls` | Request custom video controls (v4.3.3+) | false |
153
+
154
+ `NativeMediaAspect` values: `UNSPECIFIED`, `ANY`, `LANDSCAPE`, `PORTRAIT`, `SQUARE`. `NativeTemplateView.setMediaAspect(...)` overrides the global default per view.
155
+
156
+ ```kotlin
157
+ AdManageKitConfig.apply {
158
+ nativeLoadingStrategy = AdLoadingStrategy.HYBRID
159
+ nativeCacheExpiry = 1.hours
160
+ defaultNativeMediaAspect = NativeMediaAspect.ANY
161
+ nativeVideoStartMuted = true
162
+ }
163
+ ```
164
+
141
165
  ### App Open Settings
142
166
 
143
167
  | Setting | Description | Default |
@@ -1,18 +1,35 @@
1
1
  # AdManageKit
2
2
 
3
3
  [![JitPack](https://jitpack.io/v/i2hammad/AdManageKit.svg)](https://jitpack.io/#i2hammad/AdManageKit)
4
- ![API](https://img.shields.io/badge/API-21%2B-brightgreen.svg?style=flat)
4
+ ![API](https://img.shields.io/badge/API-24%2B-brightgreen.svg?style=flat)
5
5
  ![License](https://img.shields.io/badge/License-MIT-blue.svg)
6
6
 
7
7
  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.
8
8
 
9
- **Latest Version: `2.8.0`**
9
+ **Latest Version: `4.4.2`**
10
10
 
11
- ## What's New in 2.8.0
11
+ ## What's New in 4.4.2
12
12
 
13
- - **Loading Strategy for AdManager**: `forceShowInterstitial()` now respects global loading strategy
14
- - **Global Auto-Reload Config**: New `interstitialAutoReload` setting with per-call override
15
- - **New Method**: `forceShowInterstitialAlways()` for explicit force fetch behavior
13
+ Bug-fix release, no API changes. Two deliberate behavior changes are noted below.
14
+
15
+ - **Rewarded ads could crash** — every show-path callback, including `onRewardEarned`, was delivered on a background thread
16
+ - **A completed purchase could fail to disable ads** until the next app launch
17
+ - **Blank gaps** where banner/native slots should have collapsed, for premium users and after a failed load, in both XML and Compose
18
+ - **App open ads could appear over excluded screens**, including flows protected by `disableAppOpenAdsTemporarily()`
19
+ - **`BannerAdView` leaked its Activity** and kept requesting ads after being detached
20
+
21
+ > **Behavior changes:** an account-hold subscription no longer disables ads (it already reported `ON_HOLD` and failed `isSubscriptionActive()`), and premium users no longer reserve ad space in Compose.
22
+
23
+ ## Recent Highlights
24
+
25
+ - **4.4.1** — Google Mobile Ads Next-Gen SDK 1.3.0 (from 1.2.1), plus repairs to API doc generation and the MCP documentation server
26
+ - **4.4.0** — Subscription offers can be **purchased individually** (`subscribe(activity, offer)`), offer lookup by id/base plan/tag, cross-cadence price normalization (`BillingPeriod`, `getSavingsPercent`), trial eligibility, Play Billing 9 one-time product offers, and client-side **account hold** detection. See [[Subscription Offers]]
27
+ - **4.3.x** — All standard banner sizes (`BannerAdSize`), custom native templates, and app-open ad freshness enforcement
28
+ - **4.2.0** — Migrated to the Google Mobile Ads **Next-Gen SDK** and Play Billing 9. `MobileAds.initialize()` must now be called explicitly before any ad request
29
+
30
+ > Upgrading from 3.x or earlier? Read the [Migrating to 4.2.0](https://github.com/i2hammad/AdManageKit#migrating-to-420) notes first — it is the one release in the 4.x line that is not source-compatible.
31
+
32
+ Full details: [Changelog](https://github.com/i2hammad/AdManageKit/blob/main/CHANGELOG.md) · [Release Notes](https://github.com/i2hammad/AdManageKit/tree/main/docs/release-notes)
16
33
 
17
34
  ## Features
18
35
 
@@ -23,14 +40,16 @@ AdManageKit is a comprehensive Android library designed to simplify the integrat
23
40
  - **App Open Ads**: Lifecycle-aware with activity exclusion
24
41
 
25
42
  ### NativeTemplateView (v2.6.0+)
26
- - 17 Template Styles: card_modern, material3, minimal, list_item, magazine, video templates
43
+ - 38 Template Styles: card_modern, material3, minimal, list_item, magazine, app_store, social_feed, spotlight, plus the video_* and flat_* families
27
44
  - XML & Programmatic: Set templates via `app:adTemplate` or `setTemplate()`
45
+ - Custom Templates (v4.3.0+): supply your own layout via `setCustomTemplate()` or `app:customAdLayout`
28
46
  - Material 3 Theming: Automatic dark/light mode support
29
47
 
30
48
  ### Ad Loading Strategies (v2.6.0+)
31
49
  - **ON_DEMAND**: Fetch fresh ads with loading dialog
32
50
  - **ONLY_CACHE**: Instant display from cache
33
51
  - **HYBRID**: Cache-first with fallback fetch (recommended)
52
+ - **FRESH_WITH_CACHE_FALLBACK**: Fetch fresh, fall back to cache on failure
34
53
 
35
54
  ### Centralized Configuration
36
55
  - **AdManageKitConfig**: Single configuration point
@@ -65,27 +84,39 @@ dependencyResolutionManagement {
65
84
  **Step 2:** Add dependencies to your app's `build.gradle`:
66
85
 
67
86
  ```groovy
68
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v2.8.0'
69
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v2.8.0'
70
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v2.8.0'
87
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v4.4.2'
88
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v4.4.2'
89
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.2'
71
90
 
72
91
  // For Jetpack Compose support
73
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v2.8.0'
92
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v4.4.2'
93
+
94
+ // For Yandex Ads multi-provider support
95
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v4.4.2'
74
96
  ```
75
97
 
76
- **Step 3:** Sync your project with Gradle.
98
+ **Step 3:** Ensure your app's `compileSdk` is **37 or higher** (required transitively as of 4.2.0).
99
+
100
+ **Step 4:** Sync your project with Gradle.
77
101
 
78
102
  ### Quick Configuration
79
103
 
80
104
  Configure AdManageKit in your Application class:
81
105
 
106
+ > **Since 4.2.0 you must call `MobileAds.initialize()` yourself.** The Next-Gen SDK removed the legacy SDK's silent lazy-init, so an app that skips it never loads ads. AdManageKit does not call it for you, because it does not own your consent flow.
107
+
82
108
  ```kotlin
83
109
  class MyApp : Application() {
84
- private lateinit var appOpenManager: AppOpenManager
110
+ var appOpenManager: AppOpenManager? = null
85
111
 
86
112
  override fun onCreate() {
87
113
  super.onCreate()
88
114
 
115
+ initAds()
116
+
117
+ // Set up billing
118
+ BillingConfig.setPurchaseProvider(BillingPurchaseProvider())
119
+
89
120
  // Configure AdManageKit
90
121
  AdManageKitConfig.apply {
91
122
  debugMode = BuildConfig.DEBUG
@@ -100,16 +131,29 @@ class MyApp : Application() {
100
131
  // Auto-reload interstitial after showing
101
132
  interstitialAutoReload = true // default: true
102
133
  }
134
+ }
103
135
 
104
- // Set up billing
105
- BillingConfig.setPurchaseProvider(BillingPurchaseProvider())
106
-
107
- // Initialize app open ads
108
- appOpenManager = AppOpenManager(this, "your-app-open-ad-unit-id")
136
+ private fun initAds() {
137
+ val config = InitializationConfig.Builder(readApplicationIdFromManifest()).build()
138
+
139
+ // initialize() blocks, so keep it off the main thread or it can ANR.
140
+ Thread {
141
+ MobileAds.initialize(this, config)
142
+
143
+ // Construct AppOpenManager only after initialize() returns. Constructing it
144
+ // arms its ProcessLifecycleOwner observer, and onStart() fires as soon as any
145
+ // activity starts — created earlier, that observer can race ahead of
146
+ // initialization and issue a load the Next-Gen SDK rejects as "not initialized".
147
+ Handler(Looper.getMainLooper()).post {
148
+ appOpenManager = AppOpenManager(this, "your-app-open-ad-unit-id")
149
+ }
150
+ }.start()
109
151
  }
110
152
  }
111
153
  ```
112
154
 
155
+ The Next-Gen SDK no longer reads the application id from the manifest automatically, so `readApplicationIdFromManifest()` pulls `com.google.android.gms.ads.APPLICATION_ID` from your `ApplicationInfo` metadata. See the sample app's [`MyApplication.kt`](https://github.com/i2hammad/AdManageKit/blob/main/app/src/main/java/com/i2hammad/admanagekit/sample/MyApplication.kt) for the full version.
156
+
113
157
  ## Wiki Pages
114
158
 
115
159
  ### Ad Types
@@ -128,6 +172,14 @@ class MyApp : Application() {
128
172
  - [[Multi-Provider Waterfall]] - Load ads from multiple networks with automatic fallback
129
173
  - [[Yandex Integration]] - Yandex Ads SDK provider setup and configuration
130
174
 
175
+ ### Billing
176
+ - [[Billing Integration]] - Play Billing setup, products, and purchase flows
177
+ - [[Purchase Categories]] - Product type classification
178
+ - [[Consumable Products]] - Consumable in-app purchases
179
+ - [[Subscriptions]] - Subscription state, renewal, and account hold
180
+ - [[Subscription Offers]] - Multi-offer paywalls, price comparison, trial eligibility
181
+ - [[Subscription Upgrades]] - Plan changes and proration modes
182
+
131
183
  ## Sample Project
132
184
 
133
185
  The `app` module demonstrates all features. To run:
@@ -1,10 +1,10 @@
1
- # Interstitial Ads - AdManageKit v2.8.0
1
+ # Interstitial Ads - AdManageKit v4.4.2
2
2
 
3
3
  ## Overview
4
4
 
5
5
  AdManageKit provides a complete interstitial ad stack with `AdManager` and the fluent `InterstitialAdBuilder`. Features include loading strategies, automatic retry, frequency controls, splash screen support, and Jetpack Compose utilities.
6
6
 
7
- **Library Version**: v2.8.0
7
+ **Library Version**: v4.4.2
8
8
 
9
9
  ## What's New
10
10
 
@@ -23,11 +23,11 @@ AdManageKit provides a complete interstitial ad stack with `AdManager` and the f
23
23
 
24
24
  ```groovy
25
25
  dependencies {
26
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v2.8.0'
27
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v2.8.0'
28
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v2.8.0'
26
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v4.4.2'
27
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.2'
28
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-billing:v4.4.2'
29
29
  // Optional - Jetpack Compose
30
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v2.8.0'
30
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v4.4.2'
31
31
  }
32
32
  ```
33
33
 
@@ -58,6 +58,8 @@ class MyApp : Application() {
58
58
  }
59
59
  ```
60
60
 
61
+ > **Since 4.2.0 you must also call `MobileAds.initialize()` yourself**, before any ad request — the Next-Gen SDK removed the legacy SDK's silent lazy-init. This snippet covers configuration only. See [[Home]] or the sample app's [`MyApplication.kt`](https://github.com/i2hammad/AdManageKit/blob/main/app/src/main/java/com/i2hammad/admanagekit/sample/MyApplication.kt) for the initialization pattern.
62
+
61
63
  ## Option 1: Direct AdManager
62
64
 
63
65
  ### Basic Usage
@@ -104,6 +106,7 @@ fun showInterstitial(activity: Activity) {
104
106
  | ON_DEMAND | Always fetch fresh ad with dialog |
105
107
  | ONLY_CACHE | Show cached if ready, skip otherwise |
106
108
  | HYBRID | Show cached if ready, fetch fresh otherwise |
109
+ | FRESH_WITH_CACHE_FALLBACK | Fetch fresh, fall back to cache if the load fails |
107
110
 
108
111
  ```kotlin
109
112
  // Set strategy
@@ -1,18 +1,18 @@
1
- # Jetpack Compose Integration - AdManageKit v2.8.0
1
+ # Jetpack Compose Integration - AdManageKit v4.4.2
2
2
 
3
3
  ## Overview
4
4
 
5
5
  AdManageKit provides first-class Jetpack Compose support with composable functions for all ad types, state management helpers, and programmatic native ad loading.
6
6
 
7
- **Library Version**: v2.8.0
7
+ **Library Version**: v4.4.2
8
8
 
9
9
  ## Installation
10
10
 
11
11
  ```groovy
12
12
  dependencies {
13
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v2.8.0'
14
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v2.8.0'
15
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v2.8.0'
13
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v4.4.2'
14
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.2'
15
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-compose:v4.4.2'
16
16
  }
17
17
  ```
18
18
 
@@ -60,6 +60,27 @@ fun MyScreen() {
60
60
  }
61
61
  ```
62
62
 
63
+ ### Custom Templates & Shimmer (v4.3.0+)
64
+
65
+ If none of the 38 built-in templates fit, pass your own layout instead of a `template`:
66
+
67
+ ```kotlin
68
+ NativeTemplateCompose(
69
+ adUnitId = "ca-app-pub-xxx/yyy",
70
+ customLayoutResId = R.layout.my_native_ad,
71
+ customShimmerResId = R.layout.my_native_ad_shimmer, // recommended, see below
72
+ customSizeHint = NativeAdSize.MEDIUM
73
+ )
74
+ ```
75
+
76
+ `customLayoutResId` takes precedence over `template`. The layout's root must be (or inflate as) a Next-Gen SDK `NativeAdView`, reusing the standard asset ids so the library can bind them: `ad_headline`, `ad_body`, `ad_call_to_action`, `ad_app_icon`, `ad_advertiser`, `ad_media`, `ad_stars`, `ad_choices_view`. Omitted ids are simply not populated, but keep the call-to-action fully visible and tappable — the native ad validator flags clipping as a policy violation.
77
+
78
+ > **`customShimmerResId` is optional, but omitting it is rarely what you want.** The shimmer then falls back to the one belonging to `template` (default `CARD_MODERN`), so a custom layout still shows a placeholder — just one shaped like a different ad, which visibly jumps when the real ad arrives. Either supply a matching shimmer, or set `template` to whichever built-in most resembles your layout so the fallback is the right shape. Passing `0` is the same as omitting it.
79
+
80
+ `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 — `SMALL` (icon + title + CTA), `MEDIUM` (adds body), `LARGE` (adds media). It does not affect how your layout is measured.
81
+
82
+ The XML and programmatic equivalents are covered in [[Native Ads|NativeAdManager]].
83
+
63
84
  ### Traditional Native Composables
64
85
 
65
86
  ```kotlin
@@ -38,11 +38,11 @@ This is fully backward compatible -- if no provider chains are configured, all e
38
38
 
39
39
  ```groovy
40
40
  dependencies {
41
- implementation "com.github.i2hammad.AdManageKit:ad-manage-kit:VERSION"
42
- implementation "com.github.i2hammad.AdManageKit:ad-manage-kit-core:VERSION"
41
+ implementation "com.github.i2hammad.AdManageKit:ad-manage-kit:v4.4.2"
42
+ implementation "com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.2"
43
43
 
44
44
  // Add Yandex provider
45
- implementation "com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:VERSION"
45
+ implementation "com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v4.4.2"
46
46
  }
47
47
  ```
48
48
 
@@ -161,7 +161,7 @@ if (isRussia) {
161
161
  All existing features continue to work when the waterfall is active:
162
162
 
163
163
  - Time-based and count-based interstitial display
164
- - Loading strategies (ON_DEMAND, ONLY_CACHE, HYBRID)
164
+ - Loading strategies (ON_DEMAND, ONLY_CACHE, HYBRID, FRESH_WITH_CACHE_FALLBACK)
165
165
  - Shimmer loading placeholders
166
166
  - Loading dialogs for interstitials
167
167
  - Welcome back dialog for app open ads
@@ -1,26 +1,26 @@
1
- # Native Ads - AdManageKit v2.8.0
1
+ # Native Ads - AdManageKit v4.4.2
2
2
 
3
3
  ## Overview
4
4
 
5
5
  AdManageKit provides comprehensive native ad support with caching, multiple formats, and the new `NativeTemplateView` (v2.6.0+). Native ads blend seamlessly with your app's content while maximizing engagement.
6
6
 
7
- **Library Version**: v2.8.0
7
+ **Library Version**: v4.4.2
8
8
 
9
9
  ## Features
10
10
 
11
- - **Multiple Formats**: Small, Medium, Large, and 17+ template styles
11
+ - **Multiple Formats**: Small, Medium, Large, and 38 template styles
12
12
  - **Smart Caching**: Per-unit caching with 1-hour expiration
13
- - **Loading Strategies**: ON_DEMAND, ONLY_CACHE, HYBRID
13
+ - **Loading Strategies**: ON_DEMAND, ONLY_CACHE, HYBRID, FRESH_WITH_CACHE_FALLBACK
14
14
  - **Shimmer Loading**: Beautiful loading placeholders
15
- - **NativeTemplateView**: Unified component with 17 templates (v2.6.0+)
15
+ - **NativeTemplateView**: Unified component with 38 templates (v2.6.0+)
16
16
  - **Video Support**: All templates support video ads
17
17
 
18
18
  ## Installation
19
19
 
20
20
  ```groovy
21
21
  dependencies {
22
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v2.8.0'
23
- implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v2.8.0'
22
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit:v4.4.2'
23
+ implementation 'com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.2'
24
24
  }
25
25
  ```
26
26
 
@@ -36,7 +36,7 @@ dependencies {
36
36
 
37
37
  ### NativeTemplateView (v2.6.0+)
38
38
 
39
- 17 template styles in one unified component:
39
+ 38 template styles in one unified component. A selection:
40
40
 
41
41
  | Template | Best For |
42
42
  |----------|----------|
@@ -49,6 +49,34 @@ dependencies {
49
49
  | `video_small/medium/large` | Video content |
50
50
  | `video_square/vertical/fullscreen` | Social feeds |
51
51
  | `featured`, `grid_card`, `overlay_dark`, `story_style` | Various layouts |
52
+ | `app_store`, `social_feed`, `gradient_card`, `spotlight` | Themed placements |
53
+ | `flat_*` family (10 styles) | Flat/minimal design systems |
54
+
55
+ See [`attrs.xml`](https://github.com/i2hammad/AdManageKit/blob/main/AdManageKit/src/main/res/values/attrs.xml) for the full `adTemplate` enum.
56
+
57
+ ### Custom Templates (v4.3.0+)
58
+
59
+ If none of the 38 presets fit, supply your own layout. The root must be a Next-Gen SDK `NativeAdView` reusing the standard asset ids (`ad_headline`, `ad_body`, `ad_call_to_action`, `ad_app_icon`, `ad_advertiser`, `ad_media`).
60
+
61
+ ```kotlin
62
+ nativeTemplateView.setCustomTemplate(
63
+ layoutResId = R.layout.my_native_ad,
64
+ shimmerResId = R.layout.my_shimmer, // optional
65
+ sizeHint = NativeAdSize.MEDIUM // optional, default MEDIUM
66
+ )
67
+ ```
68
+
69
+ Or in XML via `app:customAdLayout` / `app:customAdShimmerLayout`, or `customLayoutResId` on `NativeTemplateCompose`.
70
+
71
+ ### Media Aspect Ratio (v4.3.3+)
72
+
73
+ Every native request carries a media aspect-ratio hint matched to the template's `MediaView` slot. Override it globally with `AdManageKitConfig.defaultNativeMediaAspect`, or per view:
74
+
75
+ ```kotlin
76
+ nativeTemplateView.setMediaAspect(NativeMediaAspect.LANDSCAPE)
77
+ ```
78
+
79
+ Values: `UNSPECIFIED`, `ANY` (default), `LANDSCAPE`, `PORTRAIT`, `SQUARE`.
52
80
 
53
81
  ## Usage
54
82
 
@@ -142,6 +170,7 @@ val cachedAd = NativeAdManager.getCachedNativeAd("ad-unit-id")
142
170
  | ON_DEMAND | Show shimmer, fetch fresh ad |
143
171
  | ONLY_CACHE | Show cached or hide container |
144
172
  | HYBRID | Show cached if ready, fetch with shimmer otherwise |
173
+ | FRESH_WITH_CACHE_FALLBACK | Fetch fresh, fall back to cache if the load fails |
145
174
 
146
175
  ```kotlin
147
176
  // Global strategy
@@ -201,7 +230,7 @@ Load → Check cache →
201
230
 
202
231
  ## Best Practices
203
232
 
204
- 1. **Use NativeTemplateView** - Unified API with 17 templates
233
+ 1. **Use NativeTemplateView** - Unified API with 38 templates
205
234
  2. **Enable Caching** - Improves load times and reduces requests
206
235
  3. **Use HYBRID Strategy** - Best balance for most cases
207
236
  4. **Clear on Destroy** - Call `clearAllCachedAds()` in `onDestroy()`
@@ -10,9 +10,9 @@ The `admanagekit-yandex` module provides Yandex Ads SDK providers for the multi-
10
10
 
11
11
  ```groovy
12
12
  dependencies {
13
- implementation "com.github.i2hammad.AdManageKit:ad-manage-kit:VERSION"
14
- implementation "com.github.i2hammad.AdManageKit:ad-manage-kit-core:VERSION"
15
- implementation "com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:VERSION"
13
+ implementation "com.github.i2hammad.AdManageKit:ad-manage-kit:v4.4.2"
14
+ implementation "com.github.i2hammad.AdManageKit:ad-manage-kit-core:v4.4.2"
15
+ implementation "com.github.i2hammad.AdManageKit:ad-manage-kit-yandex:v4.4.2"
16
16
  }
17
17
  ```
18
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "admanagekit-mcp-server",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "MCP server for AdManageKit Android library - documentation lookup and code generation",
5
5
  "main": "dist/index.js",
6
6
  "bin": {