Skip to content

feat: add kotlin-example module using android-sdk-framework directly#254

Draft
typotter wants to merge 1 commit intosnapshot/typo/apr-17from
muse/kotlin-framework-example-app
Draft

feat: add kotlin-example module using android-sdk-framework directly#254
typotter wants to merge 1 commit intosnapshot/typo/apr-17from
muse/kotlin-framework-example-app

Conversation

@typotter
Copy link
Copy Markdown
Collaborator

Summary

New :kotlin-example Android application module demonstrating how to use :android-sdk-framework directly (without going through :eppo). Written in Kotlin, uses kotlinx.serialization for JSON flag values and OkHttp 4.x for HTTP.

Stacked on snapshot/typo/apr-17.

What's in this PR

New module: :kotlin-example

File Purpose
KotlinxConfigurationParser ConfigurationParser<JsonElement>parseJsonValue uses kotlinx.serialization; parseFlagConfig/parseBanditParams delegated to JacksonConfigurationParser (framework DTOs have hand-rolled Jackson deserializers)
EppoApplication Wires BaseAndroidClient.Builder with KotlinxConfigurationParser + OkHttpEppoClient in Application.onCreate()
MainActivity Shows flag assignment result via getStringAssignment()

Changed: CachingConfigurationStore.seedCache() visibility

Made public (was package-private). BaseAndroidClient and CachingConfigurationStore are in different packages so package-private was inaccessible.

Dependency note

Uses sdk-common-jvm:4.0.0-SNAPSHOT not 3.13.1. The old 3.13.1 monolithic jar bundles eppo-sdk-framework classes and produces duplicate class errors when combined with eppo-sdk-framework:0.1.0-SNAPSHOT from :android-sdk-framework.

Resolves #253

New :kotlin-example Android app demonstrating direct use of
:android-sdk-framework (not :eppo).

- KotlinxConfigurationParser implements ConfigurationParser<JsonElement>
  using kotlinx.serialization for parseJsonValue; delegates parseFlagConfig
  and parseBanditParams to JacksonConfigurationParser (framework DTOs have
  hand-rolled Jackson deserializers in sdk-common-jvm)
- OkHttpEppoClient from sdk-common-jvm:4.0.0-SNAPSHOT for HTTP (OkHttp 4.x)
- BaseAndroidClient.Builder wired in EppoApplication.onCreate()
- MainActivity shows flag assignment result via getStringAssignment()
- Make CachingConfigurationStore.seedCache() public (was package-private,
  inaccessible from BaseAndroidClient in a different package)
Copy link
Copy Markdown
Contributor

@aarsilv aarsilv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work proving this out in an example!

* format. Only [parseJsonValue] uses kotlinx.serialization, since that is the method that
* returns the user-facing [JsonElement] type.
*/
class KotlinxConfigurationParser : ConfigurationParser<JsonElement> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Kotlin sample Android app module (:kotlin-example) that demonstrates using :android-sdk-framework directly (w/ kotlinx.serialization for JSON flag values and OkHttp 4.x for HTTP), and adjusts framework visibility to support this usage.

Changes:

  • Add new :kotlin-example Android application module (Kotlin + kotlinx.serialization + OkHttp).
  • Wire BaseAndroidClient.Builder using KotlinxConfigurationParser and OkHttpEppoClient.
  • Make CachingConfigurationStore.seedCache() public to allow cross-package use from BaseAndroidClient.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
settings.gradle Includes the new :kotlin-example module in the build.
build.gradle Adds Kotlin Android + Kotlin serialization plugins at the root.
android-sdk-framework/src/main/java/cloud/eppo/android/framework/storage/CachingConfigurationStore.java Makes seedCache() public for use outside the storage package.
kotlin-example/build.gradle Defines the new app module, dependencies (OkHttp, kotlinx.serialization), and BuildConfig API key wiring.
kotlin-example/src/main/AndroidManifest.xml Declares the sample app, EppoApplication, and launcher activity.
kotlin-example/src/main/kotlin/cloud/eppo/kotlinexample/EppoApplication.kt Initializes BaseAndroidClient asynchronously using the framework + OkHttp client.
kotlin-example/src/main/kotlin/cloud/eppo/kotlinexample/MainActivity.kt Displays a sample string assignment result.
kotlin-example/src/main/kotlin/cloud/eppo/kotlinexample/KotlinxConfigurationParser.kt Implements ConfigurationParser<JsonElement> using kotlinx.serialization for JSON values.
kotlin-example/src/main/res/layout/activity_main.xml Basic UI for status + assignment display.
kotlin-example/src/main/res/values/colors.xml Sample theme colors.
kotlin-example/src/main/res/values/strings.xml App strings for UI text.
kotlin-example/src/main/res/values/themes.xml MaterialComponents theme for the sample app.
kotlin-example/proguard-rules.pro Placeholder ProGuard config for the module.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localProperties.load(new FileInputStream(localPropertiesFile))
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Gradle script loads local.properties via new FileInputStream(...) but never closes the stream. In long-lived Gradle daemons this can leak file descriptors. Use localPropertiesFile.withInputStream { localProperties.load(it) } (or a try-with-resources equivalent) to ensure the stream is closed.

Suggested change
localProperties.load(new FileInputStream(localPropertiesFile))
localPropertiesFile.withInputStream { localProperties.load(it) }

Copilot uses AI. Check for mistakes.
try {
json.parseToJsonElement(jsonValue)
} catch (e: Exception) {
throw ConfigurationParseException("Failed to parse JSON value: $jsonValue", e)
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thrown ConfigurationParseException message includes the full jsonValue string. This can unintentionally leak sensitive flag payloads into logs/crash reports and can also be very large. Prefer omitting the raw value (or logging a truncated/length-only version) and rely on the cause exception for debugging details.

Suggested change
throw ConfigurationParseException("Failed to parse JSON value: $jsonValue", e)
throw ConfigurationParseException(
"Failed to parse JSON value (length=${jsonValue.length})",
e,
)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants