diff --git a/CHANGELOG.md b/CHANGELOG.md index 0643d46..c205cb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.0.5 + +OMID compliance + Kotlin 2.1 baseline. + +* `OmSession`: switch HTML display impression owner from `Owner.NATIVE` to `Owner.JAVASCRIPT` (was wrongly assumed to suppress JS geometry polling — IAB validator flagged the resulting impression events). Now matches v3 sdk-kotlin, kontextkit-ios, and the IAB OMID Android v1.6.4 reference demo: `Owner.JAVASCRIPT` for impression on both display and video; `mediaEventsOwner` stays NONE for display, JAVASCRIPT for video. Native-side `AdEvents` construction dropped — JS verification scripts now own `loaded()` + `impressionOccurred()` for both creative types. `OmSession.loaded()` / `impressionOccurred()` kept as no-ops for SDK API compatibility (consuming SDKs still reference them). +* Toolchain: Kotlin 1.9.22 → **2.1.0**, AGP 8.6.1 → 8.7.3, Gradle 8.7 → 8.9, detekt 1.23.4 → 1.23.8, spotless 6.25.0 → 7.2.1. Aligns with sdk-kotlin 2.1 baseline so consuming apps already on Kotlin 2.x stop hitting compiler-output / metadata mismatches. + ## 0.0.4 * `InstallIdProvider`: new `deviceinfo/InstallIdProvider.kt`. Returns a UUID v7 per-app-install identifier persisted in a dedicated `kontextso` `SharedPreferences` file under the `installId` key. Generated on first call, survives launches, resets only on uninstall / app-data clear. Mirrors iOS `InstallIdProvider` (UserDefaults-backed) so consumer SDKs can thread the same `installId` field through `/init`, `/preload`, `/error`, and `/debug` request payloads. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 02f94b3..7c7eaf5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,16 +14,16 @@ # `./gradlew publish -PkitVersion=1.1.0` so CI can cut releases # without bumping this file. Consumed by build.gradle.kts as the # fallback when the `kitVersion` Gradle property is absent. -kit = "0.0.4" +kit = "0.0.5" # ---- Build tooling ---- -agp = "8.6.1" # Android Gradle Plugin -kotlin = "1.9.22" # Kotlin compiler version +agp = "8.7.3" # Android Gradle Plugin +kotlin = "2.1.0" # Kotlin compiler version # ---- Code-quality plugins ---- maven-publish = "0.34.0" # vanniktech maven-publish -spotless = "6.25.0" -detekt = "1.23.4" +spotless = "7.2.1" +detekt = "1.23.8" ktlint = "0.50.0" # consumed inside spotless { ktlint(...) } # ---- AndroidX ---- diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7b71216..fff113c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Aug 11 16:57:10 CEST 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/kotlin/so/kontext/kit/omsdk/OmSession.kt b/src/main/kotlin/so/kontext/kit/omsdk/OmSession.kt index 1a8efcb..5961869 100644 --- a/src/main/kotlin/so/kontext/kit/omsdk/OmSession.kt +++ b/src/main/kotlin/so/kontext/kit/omsdk/OmSession.kt @@ -39,8 +39,6 @@ public class OmSession( private var session: Any? = null private var adEvents: Any? = null private var started = false - private var loadedFired = false - private var impressionFired = false public val isValid: Boolean get() = session != null @@ -68,30 +66,32 @@ public class OmSession( val context = createContext.invoke(null, partner, webView, url, "") // Pick creative-type / impression-type / impression-owner / media-events-owner. - // - Display: NATIVE impression owner. SDK fires loaded() + impressionOccurred() - // via AdEvents so the JS verification script does NOT poll geometry and - // does NOT emit a `notFound` geometryChange when the WebView detaches. - // - Video: DEFINED_BY_JAVASCRIPT triple per the IAB OMID Android - // #webview-video docs — JS owns impression + media events. - val nativeOwner = ownerClass.getField("NATIVE").get(null)!! + // Matches v3 sdk-kotlin (`internal/utils/om/WebViewOmSession.kt`) and + // kontextkit-ios (`Sources/OMSDK/OMManager.swift`) — both use + // `Owner.JAVASCRIPT` for the impression owner regardless of creative. + // The JS verification script (use-omid-display-session / + // use-omid-video-session in ads/packages/omsdk) owns firing + // `loaded()` + `impressionOccurred()` from inside the iframe; the + // native side just creates the session. + // - Display: HTML_DISPLAY + BEGIN_TO_RENDER + JS impression + NONE media. + // - Video: DEFINED_BY_JAVASCRIPT triple per the IAB OMID Android + // #webview-video docs. val jsOwner = ownerClass.getField("JAVASCRIPT").get(null)!! val noneOwner = ownerClass.getField("NONE").get(null)!! val omCreativeType: Any val omImpressionType: Any - val impressionOwner: Any val mediaOwner: Any if (creativeType == OmCreativeType.VIDEO) { omCreativeType = creativeTypeClass.getField("DEFINED_BY_JAVASCRIPT").get(null)!! omImpressionType = impressionTypeClass.getField("DEFINED_BY_JAVASCRIPT").get(null)!! - impressionOwner = jsOwner mediaOwner = jsOwner } else { omCreativeType = creativeTypeClass.getField("HTML_DISPLAY").get(null)!! omImpressionType = impressionTypeClass.getField("BEGIN_TO_RENDER").get(null)!! - impressionOwner = nativeOwner mediaOwner = noneOwner } + val impressionOwner: Any = jsOwner // AdSessionConfiguration.createAdSessionConfiguration( // creative, impression, impressionOwner, mediaOwner, isolateVerificationScripts=false @@ -123,15 +123,9 @@ public class OmSession( session = sess - // For NATIVE impression-owner sessions (display), create AdEvents so the - // SDK can fire loaded() + impressionOccurred() from Kotlin. For JS-owner - // sessions (video) AdEvents is still created but loaded/impression are - // owned by the in-iframe verification script and we don't call them. - if (creativeType != OmCreativeType.VIDEO) { - val adEventsClass = Class.forName("com.iab.omid.library.kontextso.adsession.AdEvents") - val createAdEvents = adEventsClass.getMethod("createAdEvents", sessionClass) - adEvents = createAdEvents.invoke(null, sess) - } + // No AdEvents created — JS verification scripts own `loaded()` + + // `impressionOccurred()` for both display and video now that + // `impressionOwner = Owner.JAVASCRIPT` is used unconditionally. } catch (e: ReflectiveOperationException) { android.util.Log.w(TAG, "OM: session init failed", e) session = null @@ -154,36 +148,21 @@ public class OmSession( } /** - * Fires the OMID `loaded` event from native code. Only valid for sessions - * with `Owner.NATIVE` as impressionOwner (display). For JS-owner sessions - * (video) the verification script emits this — calling here is a no-op. - * Must be called after [start]. + * Kept for SDK API compatibility — no-op. All sessions use + * `Owner.JAVASCRIPT` for the impression owner, so `loaded` is fired by + * the JS verification script inside the iframe (see + * `use-omid-display-session` / `use-omid-video-session` in + * ads/packages/omsdk). The native side does not own this event. */ public fun loaded() { - if (loadedFired) return - val ev = adEvents ?: return - try { - ev.javaClass.getMethod("loaded").invoke(ev) - loadedFired = true - } catch (e: ReflectiveOperationException) { - android.util.Log.w(TAG, "OM: AdEvents.loaded failed", e) - } + // intentionally empty — JS owns impressionOwner } /** - * Fires the OMID `impressionOccurred` event from native code. Only valid - * for sessions with `Owner.NATIVE` as impressionOwner (display). Must be - * called after [loaded] and after the ad is actually rendered. + * Kept for SDK API compatibility — no-op. See [loaded]. */ public fun impressionOccurred() { - if (impressionFired) return - val ev = adEvents ?: return - try { - ev.javaClass.getMethod("impressionOccurred").invoke(ev) - impressionFired = true - } catch (e: ReflectiveOperationException) { - android.util.Log.w(TAG, "OM: AdEvents.impressionOccurred failed", e) - } + // intentionally empty — JS owns impressionOwner } public fun retire() {