diff --git a/.claude/conventions.yml b/.claude/conventions.yml new file mode 100644 index 000000000..b4c353abc --- /dev/null +++ b/.claude/conventions.yml @@ -0,0 +1,38 @@ +conventions: + - id: adapter-structure + description: > + Each network adapter lives under ThirdPartyAdapters/{Network}/ as a + Gradle Android Library module. The main adapter class implements Google + mediation interfaces. Supporting classes include Manager, Initializer, + and format-specific callback classes. + applies_to: "ThirdPartyAdapters/**/*.java" + + - id: naming-conventions + description: > + Adapter main class: {Network}MediationAdapter. Initializer: + {Network}Initializer. Manager: {Network}Manager. Callback classes + named by format (e.g., {Network}RewardedAd, {Network}InterstitialAd). + All classes use Java naming conventions. + applies_to: "ThirdPartyAdapters/**/*.java" + + - id: ad-format-support + description: > + Adapters support Rewarded Video, Interstitial, and Banner formats. + Each format has its own callback/listener class. Adapters may support + both legacy and new mediation framework interfaces. + applies_to: "ThirdPartyAdapters/**/*.java" + + - id: version-management + description: > + Adapter versions encode the underlying SDK version plus an adapter + patch digit (e.g., 6.9.1.0). Version is declared in the adapter's + build.gradle and published via Maven/Bintray. CHANGELOG.md tracks + version history per adapter. + applies_to: "**/build.gradle" + + - id: gradle-build + description: > + Each adapter is a Gradle Android Library module with its own + build.gradle. The root project uses AGP 3.5.4. Publishing + configuration targets Maven/Bintray repositories. + applies_to: "**/build.gradle" diff --git a/.claude/knowledge/adrs/001-google-mediation-interface.md b/.claude/knowledge/adrs/001-google-mediation-interface.md new file mode 100644 index 000000000..075b10bec --- /dev/null +++ b/.claude/knowledge/adrs/001-google-mediation-interface.md @@ -0,0 +1,23 @@ +# ADR-001: Google Mediation Adapter Interface + +## Status +Accepted + +## Context +Google Mobile Ads SDK mediates ads from third-party networks on Android. Each network adapter must conform to Google's mediation interfaces so the Google SDK can load and display ads uniformly regardless of the underlying network. + +## Decision +Adapters implement Google's mediation interfaces: +- `MediationInterstitialAdapter` for interstitial ads +- `MediationBannerAdapter` for banner ads +- `MediationRewardedVideoAdAdapter` for rewarded video ads +- Newer `Adapter` base class for the updated mediation framework + +The main adapter class (`{Network}MediationAdapter`) serves as the entry point. It delegates to format-specific classes that handle loading, displaying, and callback forwarding for each ad type. + +## Consequences +- Strict conformance to Google's interface is required; no custom extensions +- Google controls the adapter lifecycle (instantiation, initialization, ad requests) +- Format-specific classes keep the main adapter manageable +- Must map network-specific error codes to Google AdMob error codes +- Adapters are instantiated by reflection, requiring a no-arg constructor diff --git a/.claude/knowledge/adrs/002-legacy-and-new-framework.md b/.claude/knowledge/adrs/002-legacy-and-new-framework.md new file mode 100644 index 000000000..50b92f047 --- /dev/null +++ b/.claude/knowledge/adrs/002-legacy-and-new-framework.md @@ -0,0 +1,21 @@ +# ADR-002: Dual Framework Support (Legacy and New) + +## Status +Accepted + +## Context +Google transitioned from a legacy mediation framework (individual format interfaces like `MediationInterstitialAdapter`) to a newer unified `Adapter` base class. During the transition period, adapters need to support both frameworks to maintain backward compatibility. + +## Decision +Adapters support both the legacy and new mediation frameworks: +- **Legacy**: Implement `MediationInterstitialAdapter`, `MediationBannerAdapter`, `MediationRewardedVideoAdAdapter` directly +- **New**: Extend the `Adapter` base class and implement `MediationInterstitialAd`, `MediationBannerAd`, `MediationRewardedAd` + +The main adapter class bridges both frameworks, delegating to appropriate format-specific classes depending on which framework path is invoked. + +## Consequences +- Backward compatibility is maintained during the framework transition +- Increased code complexity from supporting two calling conventions +- Format-specific classes may have legacy and new variants +- Eventually legacy support can be removed once migration is complete +- Testing must cover both framework paths diff --git a/.claude/knowledge/adrs/003-singleton-initialization.md b/.claude/knowledge/adrs/003-singleton-initialization.md new file mode 100644 index 000000000..a6d73297c --- /dev/null +++ b/.claude/knowledge/adrs/003-singleton-initialization.md @@ -0,0 +1,22 @@ +# ADR-003: Singleton Initialization Pattern (VungleInitializer) + +## Status +Accepted + +## Context +The Vungle SDK (and many other network SDKs) must be initialized exactly once before any ad requests. Multiple adapter instances for different ad formats may attempt initialization concurrently. The Google mediation SDK may create multiple adapter instances. + +## Decision +A dedicated `VungleInitializer` singleton manages SDK initialization: +- Private constructor with static `getInstance()` access +- Thread-safe initialization state tracking +- Queues initialization callbacks when init is already in progress +- Caches the initialization result to immediately callback on subsequent requests +- `VungleManager` handles shared SDK state and placement management separately + +## Consequences +- SDK is initialized exactly once regardless of how many adapter instances exist +- Concurrent initialization requests are safely queued +- Clear separation: Initializer handles init lifecycle, Manager handles ad state +- Singleton pattern makes unit testing harder (requires reset/mock capability) +- All adapter instances share the same initialization state diff --git a/.claude/knowledge/computed/adapter-class-index.md b/.claude/knowledge/computed/adapter-class-index.md new file mode 100644 index 000000000..9cd02090f --- /dev/null +++ b/.claude/knowledge/computed/adapter-class-index.md @@ -0,0 +1,55 @@ +# Google Mobile Ads (GMA) — Vungle Android Mediation Adapter Class Index + +## Adapter Entry Point + +| Class | Superclass | Package | +|-------|-----------|---------| +| `VungleMediationAdapter` | `Adapter` (Google) | `com.google.ads.mediation.vungle` | + +**Initialization**: `initialize(context, initConfig, callback)` → calls `VungleAds.init(context, appId)` +**Singleton**: `VungleInitializer` manages shared initialization state across all format instances + +## Format Classes + +| Format | Class | Google Interface | +|--------|-------|-----------------| +| Interstitial | `VungleInterstitialAdapter` | `MediationInterstitialAd` | +| Rewarded | `VungleRewardedAdapter` | `MediationRewardedAd` | +| Banner | `VungleBannerAdapter` | `MediationBannerAd` | + +## VungleInitializer (Singleton) + +``` +VungleInitializer.getInstance() + └─ initialize(context, appId, callback) + ├─ Already initialized → immediate callback + ├─ Initializing → queue callback + └─ Not initialized → VungleAds.init() + queue callback +``` + +## Callback Mapping (Vungle → Google GMA) + +| Vungle Callback | GMA Callback | Context | +|----------------|-------------|---------| +| `onAdLoaded(ad)` | `onSuccess(adapter)` | Load success | +| `onAdFailedToLoad(ad, error)` | `onFailure(adError)` | Load failure | +| `onAdStart(ad)` | `reportAdImpression()` | Impression | +| `onAdClicked(ad)` | `reportAdClicked()` | Click | +| `onAdEnd(ad)` | `onAdDismissedFullScreenContent()` | Fullscreen closed | +| `onAdRewarded(ad)` | `onUserEarnedReward(rewardItem)` | Reward granted | +| `onAdFailedToPlay(ad, error)` | `onAdFailedToShowFullScreenContent(error)` | Show failure | + +## Bidding Support + +- Implements `RtbAdapter` for real-time bidding +- `collectSignals(signalConfig, callback)` → `VungleAds.getBiddingToken(context)` +- `loadRtbInterstitialAd()` / `loadRtbRewardedAd()` / `loadRtbBannerAd()` for bid response loading + +## Key Patterns + +1. **Singleton VungleInitializer**: Thread-safe initialization with callback queuing, prevents duplicate init calls +2. **RtbAdapter**: Extends base `Adapter` with RTB signal collection for Google Open Bidding +3. **Error code mapping**: Vungle error codes mapped to `AdError` with Google-standard codes +4. **Banner size mapping**: Google `AdSize` → Vungle `BannerAdSize` via `VungleBannerAdSizeUtil` +5. **Server parameters**: App ID and placement ID extracted from `MediationConfiguration.getServerParameters()` +6. **Privacy**: GDPR/CCPA/COPPA forwarded via `VunglePrivacySettings` from mediation extras diff --git a/.claude/knowledge/computed/churn-leaders.md b/.claude/knowledge/computed/churn-leaders.md new file mode 100644 index 000000000..4aae85831 --- /dev/null +++ b/.claude/knowledge/computed/churn-leaders.md @@ -0,0 +1,16 @@ +# Churn Leaders +# Auto-generated — do not edit manually + +## Files With Most Lines Changed (last 90 days) + +| Rank | File | Lines Added | Lines Removed | Net | +|------|------|------------|--------------|-----| +| - | `CLAUDE.md` | +39 | -0 | 39 | +| - | `.claude/conventions.yml` | +38 | -0 | 38 | +| - | `.claude/review-rules.yml` | +27 | -0 | 27 | +| - | `.claude/knowledge/adrs/001-google-mediation-interface.md` | +23 | -0 | 23 | +| - | `.claude/knowledge/adrs/003-singleton-initialization.md` | +22 | -0 | 22 | +| - | `.claude/knowledge/adrs/002-legacy-and-new-framework.md` | +21 | -0 | 21 | + +--- +_Generated: 2026-03-24 | Period: 2025-12-24 to 2026-03-24 | Commits: 1 | Authors: 0_ diff --git a/.claude/knowledge/computed/co-change-clusters.md b/.claude/knowledge/computed/co-change-clusters.md new file mode 100644 index 000000000..4f05d7a8f --- /dev/null +++ b/.claude/knowledge/computed/co-change-clusters.md @@ -0,0 +1,8 @@ +# Co-Change Clusters +# Auto-generated — do not edit manually + +## Files That Frequently Change Together (last 90 days) + + +--- +_Generated: 2026-03-24 | Period: 2025-12-24 to 2026-03-24 | Commits: 1 | Authors: 0_ diff --git a/.claude/knowledge/computed/contributor-summary.md b/.claude/knowledge/computed/contributor-summary.md new file mode 100644 index 000000000..17446b64d --- /dev/null +++ b/.claude/knowledge/computed/contributor-summary.md @@ -0,0 +1,10 @@ +# Contributor Summary +# Auto-generated — do not edit manually + +## Top Contributors (last 90 days) + +| Author | Commits | Files Touched | +|--------|---------|--------------| + +--- +_Generated: 2026-03-24 | Period: 2025-12-24 to 2026-03-24 | Commits: 1 | Authors: 0_ diff --git a/.claude/knowledge/computed/git-intelligence.json b/.claude/knowledge/computed/git-intelligence.json new file mode 100644 index 000000000..efbcb7628 --- /dev/null +++ b/.claude/knowledge/computed/git-intelligence.json @@ -0,0 +1,24 @@ +{ + "generated": "2026-03-23T00:00:00Z", + "period_days": 180, + "since": "2025-09-24", + "recent_activity": { + "total_commits": 1, + "unique_authors": 1, + "fix_bug_commits": 0, + "first_commit_date": "2026-03-20", + "last_commit_date": "2026-03-20", + "commits_per_month": { + "2026-03": 1 + } + }, + "top_authors": [ + {"author": "Mian Leow", "commits": 1} + ], + "file_churn": [ + {"file": "CLAUDE.md", "commits": 1}, {"file": ".claude/review-rules.yml", "commits": 1}, {"file": ".claude/knowledge/adrs/003-singleton-initialization.md", "commits": 1}, {"file": ".claude/knowledge/adrs/002-legacy-and-new-framework.md", "commits": 1}, {"file": ".claude/knowledge/adrs/001-google-mediation-interface.md", "commits": 1}, {"file": ".claude/conventions.yml", "commits": 1} + ], + "bug_hotspots": [ + + ] +} diff --git a/.claude/knowledge/computed/hotspot-map.md b/.claude/knowledge/computed/hotspot-map.md new file mode 100644 index 000000000..04cb4a678 --- /dev/null +++ b/.claude/knowledge/computed/hotspot-map.md @@ -0,0 +1,17 @@ +# Git Hotspot Map +# Auto-generated — do not edit manually +# Re-run: .claude/scripts/git-hotspots.sh + +## Most Frequently Changed Files (last 90 days) + +| Rank | File | Commits | Last Changed | +|------|------|---------|-------------| +| - | `CLAUDE.md` | 1 | 2026-03-20 | +| - | `.claude/review-rules.yml` | 1 | 2026-03-20 | +| - | `.claude/knowledge/adrs/003-singleton-initialization.md` | 1 | 2026-03-20 | +| - | `.claude/knowledge/adrs/002-legacy-and-new-framework.md` | 1 | 2026-03-20 | +| - | `.claude/knowledge/adrs/001-google-mediation-interface.md` | 1 | 2026-03-20 | +| - | `.claude/conventions.yml` | 1 | 2026-03-20 | + +--- +_Generated: 2026-03-24 | Period: 2025-12-24 to 2026-03-24 | Commits: 1 | Authors: 0_ diff --git a/.claude/knowledge/computed/meta.yml b/.claude/knowledge/computed/meta.yml new file mode 100644 index 000000000..de679b3dd --- /dev/null +++ b/.claude/knowledge/computed/meta.yml @@ -0,0 +1,8 @@ +# Claude knowledge layer metadata +generated: 2026-03-23T00:00:00Z +source_commit: 71043b4dad7e87cf866e28783492471cdb8fca35 +stale_if_older_than: 7d +artifacts: + git-intelligence.json: { bytes: 842, tokens: ~210 } +total_bytes: 842 +total_tokens: ~210 diff --git a/.claude/review-rules.yml b/.claude/review-rules.yml new file mode 100644 index 000000000..24b9eab82 --- /dev/null +++ b/.claude/review-rules.yml @@ -0,0 +1,27 @@ +review_rules: + - path: "ThirdPartyAdapters/**/*.java" + rules: + - Adapter must implement the correct Google mediation interfaces for supported formats + - SDK initialization must go through the singleton Initializer class + - All ad callbacks must be invoked on the main thread + - Null checks required before invoking mediation callbacks + - Error handling must map network-specific errors to AdMob error codes + + - path: "**/build.gradle" + rules: + - compileSdkVersion and targetSdkVersion must match project standards + - minSdkVersion must be 16 or as required by the network SDK + - Google Mobile Ads SDK dependency version must be compatible + - Network SDK dependency version must match adapter version scheme + - No snapshot dependencies in release configurations + + - path: "ThirdPartyAdapters/**/Initializer*.java" + rules: + - Initializer must be a singleton (private constructor, static instance) + - Must handle concurrent initialization requests safely + - Must cache initialization state to avoid redundant init calls + + - path: "**/CHANGELOG.md" + rules: + - Every version bump must have a corresponding CHANGELOG entry + - Entries should note SDK version updates and behavioral changes diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..0a2150fc9 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,39 @@ +# Google Ads Mobile Android Mediation + +## Overview +Google-maintained Android mediation adapters for the Google Mobile Ads SDK. Contains 21 third-party network adapters enabling mediation through AdMob/Ad Manager. + +## Language +- **Java** (100%) + +## Build System +- **Gradle** with Android Gradle Plugin 3.5.4 +- Android Library modules per adapter +- Maven/Bintray publishing for distribution + +## Architecture +- Directory structure: `ThirdPartyAdapters/{Network}/` with adapter module +- Adapters implement Google mediation interfaces: `MediationInterstitialAdapter`, `MediationBannerAdapter`, `MediationRewardedVideoAdAdapter` +- Ad formats: Rewarded Video, Interstitial, Banner + +## Vungle Adapter +- Main class: `VungleMediationAdapter` +- Supporting classes: `VungleManager` (SDK lifecycle), `VungleInitializer` (singleton init), format-specific callback classes +- Adapter version: 6.9.1.0 +- Pattern: Adapter + Manager + Initializer + per-format callbacks + +## Platform Requirements +- Target SDK: 29 +- Min SDK: 16 +- Google Mobile Ads SDK: 19.7.0 + +## Example App +- Sample adapter implementation +- Custom event example +- Test application for manual verification + +## Key Conventions +- One module per network under `ThirdPartyAdapters/` +- Singleton initialization pattern via dedicated Initializer class +- Manager class handles shared SDK lifecycle +- Format-specific callback classes (not inline)