Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ jobs:
distribution: 'temurin'
java-version: '17'

- name: Setup Android SDK
uses: android-actions/setup-android@v3

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ jobs:
distribution: 'temurin'
java-version: '17'

- name: Setup Android SDK
uses: android-actions/setup-android@v3

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## Unreleased

Open Measurement moves to its own artifact, as kontextkit-ios has `KontextKitOM`.

### Breaking
* `OmManager`, `OmSession`, `OmPartner`, `OmCreativeType` and the bundled `omsdk_v1.js` are in the new `:om` module, published as `so.kontext.kit:kontext-kit-android-om` with the kit's version. Package and class names are unchanged: an SDK that measures with OMID adds `implementation("so.kontext.kit:kontext-kit-android-om:<version>")` next to the kit and changes nothing else. `kontext-kit-android` itself no longer carries the IAB OMSDK AAR, so an SDK without OMID (the video SDK) ships no OMSDK binary and takes on no OM licence.
* `kontext-kit-android` declares `kotlinx-coroutines-android`, which `AdvertisingIdProvider` already used without declaring it.

## 0.0.8

Fix an OMID activation crash when `createSession()` is called off the main thread.
Expand Down
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ Two suffixes carry meaning. Pick whichever fits the code's *shape*, not its doma
```
src/main/kotlin/so/kontext/kit/
deviceinfo/ AdvertisingIdProvider, App/HW/OS/Screen/Battery/Audio/NetworkInfoProvider, BrightnessManager
omsdk/ OmManager, OmSession, OmPartner, OmCreativeType
privacy/ TCFDataProvider
ui/ InAppBrowserManager
src/main/res/raw/omsdk_v1.js bundled OMID JS for WebView injection
om/ IAB Open Measurement: omsdk/ (OmManager, OmSession, OmPartner, OmCreativeType) and
res/raw/omsdk_v1.js; published as kontext-kit-android-om
src/main/AndroidManifest.xml declares ACCESS_NETWORK_STATE + AD_ID perms
src/test/kotlin/ mirrors main/ layout (JUnit 4 + Robolectric)
local-maven/iab/omsdk-android/ vendored IAB OMID AAR (1.6.4), loaded reflectively at runtime
Expand Down Expand Up @@ -73,7 +73,7 @@ Versioning: semver. The published version is set via `gradle/libs.versions.toml`
- **Kotlin 2.1.0**, JDK 17, AGP 8.7.3, `compileSdk = 34`, `minSdk = 24`. AndroidX-only (Jetifier off).
- **`explicitApi()` mode is on** — every top-level and class member must spell out its visibility (`public` / `internal` / `private`) and public functions need an explicit return type. Catches accidentally-public helpers before they become committed surface area.
- **No comments** unless the why is non-obvious (hidden constraint, subtle invariant, workaround for a specific bug, behavior that would surprise a reader). Don't ref PRs or tickets in code.
- **OMID AAR is loaded reflectively** by `OmManager` — it isn't declared as a Gradle dep because AAR transform fails in CI unit tests. Consuming apps wire the AAR in their own build.
- **OMID lives in `:om`** (`kontext-kit-android-om`): the kit itself has no OMSDK dependency. `OmManager` loads the OMID classes reflectively; `:om` declares `:omsdk-android` at runtime scope so they reach the host APK.

## Related repos

Expand Down
20 changes: 4 additions & 16 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,9 @@ dependencies {
// Required by AdvertisingIdProvider (GAID via Play Services)
implementation(libs.play.services.ads.identifier)

// IAB OMID Android — `OmManager` accesses OMID purely via reflection,
// but the classes have to be on the runtime classpath of the host app
// for the reflective loader to succeed (otherwise OMID gracefully
// degrades to no-op session lifecycle, killing measurement coverage).
// Declaring as `implementation` here means the IAB coordinate ends up
// in our published POM at `runtime` scope, so AGP merges the AAR into
// the final APK without exposing OMID's API surface on the consumer's
// compile classpath. Same UX as kontextkit-ios shipping the OMSDK
// xcframework inside the podspec.
//
// The `:omsdk-android` subproject republishes the unmodified IAB AAR
// as `so.kontext.iab:omsdk-android:1.6.4` (see its build.gradle.kts).
// Vanniktech maven-publish translates this `project(...)` dep to the
// Maven coordinate in the published POM.
implementation(project(":omsdk-android"))
// AdvertisingIdProvider's suspend read runs on Dispatchers.IO.
implementation(libs.kotlinx.coroutines.android)
// Open Measurement lives in `:om` (`kontext-kit-android-om`): the kit itself carries no OMSDK binary.

// Tests use JUnit 4 + Robolectric (matches v3 sdk-kotlin's :ads test stack).
testImplementation(libs.junit)
Expand Down Expand Up @@ -98,7 +86,7 @@ mavenPublishing {

pom {
name.set("Kontext Kit Android")
description.set("Shared Android primitives (device info, privacy, in-app browser, OMID) for the Kontext Kotlin / React Native / Flutter SDKs.")
description.set("Shared Android primitives (device info, privacy, in-app browser) for the Kontext Kotlin / React Native / Flutter SDKs.")
url.set("https://www.kontext.so/advertisers")

licenses {
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ play-services-ads-identifier = { module = "com.google.android.gms:play-services-
junit = { module = "junit:junit", version.ref = "junit" }
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test-core" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" }


Expand Down
136 changes: 136 additions & 0 deletions om/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.maven.publish)
alias(libs.plugins.spotless)
alias(libs.plugins.detekt)
}

// Maven coordinates published to Maven Central as
// `so.kontext.kit:kontext-kit-android-om:<kitVersion>`: IAB Open Measurement
// on top of the kit, released with it under the same version. Only the SDKs
// that measure with OMID depend on it.
//
// The published version is `libs.versions.kit` (gradle/libs.versions.toml)
// unless overridden by `-PkitVersion=X.Y.Z` at publish time (CI does this
// from the git tag — see .github/workflows/publish.yml).
val kitVersion = providers.gradleProperty("kitVersion").orElse(libs.versions.kit.get())
group = "so.kontext.kit"
version = kitVersion.get()

android {
namespace = "so.kontext.kit.om"
compileSdk = 34
defaultConfig {
minSdk = 24
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}

testOptions {
unitTests.isIncludeAndroidResources = true
unitTests.isReturnDefaultValues = true
}
}

// Strict API mode — every top-level / class member must spell out its
// visibility (`public` / `internal` / `private`) and public functions need
// an explicit return type. Catches accidentally-public helpers before they
// become committed surface area on the published artifact.
kotlin {
explicitApi()
}

dependencies {
// The kit's types (Context helpers) are part of this module's API.
api(project(":"))
// OmManager.createSession is suspend and runs its WebView work on the main dispatcher.
implementation(libs.kotlinx.coroutines.android)
// IAB OMID Android. `OmManager` accesses OMID purely via reflection,
// but the classes have to be on the runtime classpath of the host app
// for the reflective loader to succeed (otherwise OMID degrades to a
// no-op session lifecycle). As `implementation`, the IAB coordinate is
// in the published POM at `runtime` scope: AGP merges the AAR into the
// APK without exposing OMID's API on the consumer's compile classpath.
// `:omsdk-android` republishes the unmodified IAB AAR as
// `so.kontext.iab:omsdk-android:1.6.4` (see its build.gradle.kts).
implementation(project(":omsdk-android"))

testImplementation(libs.junit)
testImplementation(libs.robolectric)
testImplementation(libs.androidx.test.core)
testImplementation(libs.kotlinx.coroutines.test)
}

// Vanniktech maven-publish — same pattern as v3 sdk-kotlin (`:ads`).
// Activates publishing to Maven Central with auto-release + signing.
// Credentials come from Gradle properties at publish time.
mavenPublishing {
publishToMavenCentral(automaticRelease = true)
// Local `publishToMavenLocal` runs without GPG creds, so don't enable
// signing then. Maven Central enforces signatures on upload — CI sets
// `signingInMemoryKey` env which flips this branch on.
val hasSigningKey = (findProperty("signingInMemoryKey") as String?) != null ||
System.getenv("ORG_GRADLE_PROJECT_signingInMemoryKey") != null
if (hasSigningKey) {
signAllPublications()
}

coordinates(
groupId = group.toString(),
artifactId = "kontext-kit-android-om",
version = version.toString(),
)

pom {
name.set("Kontext Kit Android: Open Measurement")
description.set("IAB Open Measurement (OMID) for the Kontext Android SDKs, on top of Kontext Kit Android.")
url.set("https://www.kontext.so/advertisers")

licenses {
license {
name.set("Apache License, Version 2.0")
url.set("https://www.apache.org/licenses/LICENSE-2.0")
distribution.set("repo")
}
}

developers {
developer {
id.set("kontext")
name.set("kontext")
}
}

scm {
url.set("https://github.com/kontextso/kontextkit-android")
connection.set("scm:git:https://github.com/kontextso/kontextkit-android.git")
developerConnection.set("scm:git:ssh://github.com/kontextso/kontextkit-android.git")
}
}
}

// Lint / style — same versions as sdk-kotlin so contributors don't have
// two ktlint/detekt baselines to chase.
spotless {
kotlin {
target("src/**/*.kt")
targetExclude("**/build/**/*.kt")
ktlint(libs.versions.ktlint.get())
}
kotlinGradle {
target("*.gradle.kts")
targetExclude("**/build/**/*.kts")
ktlint(libs.versions.ktlint.get())
}
}

detekt {
buildUponDefaultConfig = true
parallel = true
}
1 change: 1 addition & 0 deletions om/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest />
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import android.os.Looper
import android.webkit.WebView
import kotlinx.coroutines.delay
import kotlinx.coroutines.suspendCancellableCoroutine
import so.kontext.kit.R
import so.kontext.kit.om.R
import kotlin.coroutines.resume

/**
Expand Down
File renamed without changes.
3 changes: 3 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,6 @@ rootProject.name = "kontext-kit-android"
// Maven Central without vendoring the AAR themselves. See
// `omsdk-android/build.gradle.kts` for the full rationale.
include(":omsdk-android")
// IAB Open Measurement on top of the kit, as kontextkit-ios has KontextKitOM: only the SDKs that measure with OMID
// depend on it, the others never ship the OMSDK binary or take on its licence.
include(":om")
Loading