--- a/core/build.gradle.kts	Wed Mar 12 21:35:19 2025 -0400
+++ b/core/build.gradle.kts	Thu Apr 03 16:08:10 2025 -0400
@@ -55,6 +55,7 @@
         commonMain.dependencies {
             implementation(libs.okio)
             implementation(libs.kotlinx.coroutines)
+            implementation(libs.kotlinx.serialization.json)
         }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/commonMain/kotlin/licensee/LicenseeLicenseInfoRepository.kt	Thu Apr 03 16:08:10 2025 -0400
@@ -0,0 +1,121 @@
+/*
+ * AboutOss is an utility library to retrieve and display
+ * opensource licenses in Android applications.
+ *
+ * Copyright (C) 2023 by Frederic-Charles Barthelery.
+ *
+ * This file is part of AboutOss.
+ *
+ * AboutOss is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AboutOss is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with AboutOss.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.geekorum.aboutoss.core.licensee
+
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.Json
+import okio.Source
+import okio.buffer
+
+class LicenseeLicenseInfoRepository(
+    private val produceInput: suspend () -> Source,
+    private val mainCoroutineDispatcher: CoroutineDispatcher,
+    private val ioCoroutineDispatcher: CoroutineDispatcher,
+) {
+
+    private var licensesInfo: Map<String, String>? = null
+
+    suspend fun getLicensesInfo(): Map<String, String> = withContext(mainCoroutineDispatcher) {
+        parseLicenses()
+        checkNotNull(licensesInfo)
+    }
+
+    suspend fun getLicenseFor(dependency: String): String = withContext(mainCoroutineDispatcher) {
+        parseLicenses()
+        checkNotNull(licensesInfo).let {
+            return@withContext it[dependency] ?: error("Dependency not found")
+        }
+    }
+
+    @OptIn(ExperimentalSerializationApi::class)
+    private suspend fun parseLicenses() = withContext(mainCoroutineDispatcher) {
+        if (licensesInfo == null) {
+            val licenses = withContext(ioCoroutineDispatcher) {
+                LicenseeParser(produceInput()).use {
+                    it.readLicensee()
+                }
+            }
+            licensesInfo = licenses
+        }
+    }
+}
+
+
+private class LicenseeParser(
+    input: Source
+): AutoCloseable {
+    private val buffered = input.buffer()
+
+    fun readLicensee(): Map<String, String> {
+        val json = Json {
+            ignoreUnknownKeys = true
+        }
+        val items: List<LicenseItem> = json.decodeFromString(buffered.readUtf8())
+
+        return items.associate {
+            val name = it.name ?: "${it.groupId}:${it.artifactId}"
+            val license = it.spdxLicenses.firstNotNullOfOrNull {
+                "${it.name}\n\n${it.url}"
+            } ?: it.unknownLicenses.firstNotNullOf {
+                "${it.name}\n\n${it.url}"
+            }
+            name to license
+        }
+    }
+
+    override fun close() {
+        buffered.close()
+    }
+}
+
+
+@Serializable
+private data class LicenseItem(
+    val groupId: String,
+    val artifactId: String,
+    val version: String,
+    val spdxLicenses: List<SpdxLicense> = emptyList(),
+    val unknownLicenses: List<UnknownLicense> = emptyList(),
+    val name: String? = null,
+    val scm: Scm? = null,
+)
+
+@Serializable
+private data class SpdxLicense(
+    val identifier: String,
+    val name: String,
+    val url: String,
+)
+
+@Serializable
+private data class UnknownLicense(
+    val name: String,
+    val url: String
+)
+
+@Serializable
+private data class Scm(
+    val url: String,
+)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/iosMain/kotlin/licensee/LicenseeLicenseInfoRepository.ios.kt	Thu Apr 03 16:08:10 2025 -0400
@@ -0,0 +1,67 @@
+/*
+ * AboutOss is an utility library to retrieve and display
+ * opensource licenses in Android applications.
+ *
+ * Copyright (C) 2023 by Frederic-Charles Barthelery.
+ *
+ * This file is part of AboutOss.
+ *
+ * AboutOss is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AboutOss is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with AboutOss.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.geekorum.aboutoss.core.licensee
+
+import kotlinx.cinterop.ExperimentalForeignApi
+import kotlinx.cinterop.addressOf
+import kotlinx.cinterop.usePinned
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.IO
+import okio.Buffer
+import platform.Foundation.NSBundle
+import platform.Foundation.NSData
+import platform.Foundation.NSFileManager
+import platform.posix.memcpy
+
+fun LicenseeLicenseInfoRepository(
+    licenseeResourcePath: String = "compose-resources/app/cash/licensee/artifacts.json",
+    mainCoroutineDispatcher: CoroutineDispatcher = Dispatchers.Main,
+    ioCoroutineDispatcher: CoroutineDispatcher = Dispatchers.IO,
+): LicenseeLicenseInfoRepository {
+    return LicenseeLicenseInfoRepository(
+        produceInput = {
+            val buffer = Buffer()
+            buffer.write(readResources(licenseeResourcePath))
+            buffer
+        },
+        mainCoroutineDispatcher = mainCoroutineDispatcher,
+        ioCoroutineDispatcher = ioCoroutineDispatcher
+    )
+}
+
+
+@OptIn(ExperimentalForeignApi::class)
+private fun readResources(path: String): ByteArray {
+    val data = readData(getPathInBundle(path))
+    return ByteArray(data.length.toInt()).apply {
+        usePinned { memcpy(it.addressOf(0), data.bytes, data.length) }
+    }
+}
+
+private fun getPathInBundle(path: String): String {
+    return NSBundle.mainBundle.resourcePath + "/$path"
+}
+
+private fun readData(path: String): NSData {
+    return NSFileManager.defaultManager().contentsAtPath(path) ?: error("Resources not found with path $path")
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/main/java/com/geekorum/aboutoss/core/licensee/LicenseeLicenseInfoRepository.android.kt	Thu Apr 03 16:08:10 2025 -0400
@@ -0,0 +1,42 @@
+/*
+ * AboutOss is an utility library to retrieve and display
+ * opensource licenses in Android applications.
+ *
+ * Copyright (C) 2023 by Frederic-Charles Barthelery.
+ *
+ * This file is part of AboutOss.
+ *
+ * AboutOss is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AboutOss is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with AboutOss.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.geekorum.aboutoss.core.licensee
+
+import android.content.res.AssetManager
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import okio.source
+
+fun LicenseeLicenseInfoRepository(
+    assetManager: AssetManager,
+    licenseeAssetsPath: String = "app/cash/licensee/artifacts.json",
+    mainCoroutineDispatcher: CoroutineDispatcher = Dispatchers.Main,
+    ioCoroutineDispatcher: CoroutineDispatcher = Dispatchers.IO,
+): LicenseeLicenseInfoRepository {
+    return LicenseeLicenseInfoRepository(
+        produceInput = {
+            assetManager.open(licenseeAssetsPath).source()
+        },
+        mainCoroutineDispatcher = mainCoroutineDispatcher,
+        ioCoroutineDispatcher = ioCoroutineDispatcher
+    )
+}
--- a/gradle/libs.versions.toml	Wed Mar 12 21:35:19 2025 -0400
+++ b/gradle/libs.versions.toml	Thu Apr 03 16:08:10 2025 -0400
@@ -27,7 +27,8 @@
 espresso-core = "3.5.1"
 appcompat = "1.6.1"
 okio = "3.3.0"
-kotlinx-coroutines = "1.6.4"
+kotlinx-coroutines = "1.10.1"
+kotlinx-serialization = "1.8.1"
 androidx-activity = "1.7.0"
 androidx-navigation = "2.5.3"
 androidx-compose-bom = "2023.04.00"
@@ -62,6 +63,8 @@
 androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
 androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
 
+kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
+
 
 [plugins]
 com-android-application = { id = "com.android.application", version.ref = "com-android-application" }
@@ -69,6 +72,7 @@
 org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "org-jetbrains-kotlin" }
 org-jetbrains-kotlin-compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "org-jetbrains-kotlin" }
 google-gms-oss-license = { id = "com.google.android.gms.oss-licenses-plugin", version = "0.10.6" }
+org-jetbrains-kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "org-jetbrains-kotlin" }
 
 [bundles]