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
16 changes: 13 additions & 3 deletions .github/workflows/sonarqube.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
name: SonarCloud Analysis

on:
push:
branches:
- master
- development
- '*_baseline'
pull_request:
branches:
- '*'
Expand Down Expand Up @@ -59,8 +64,13 @@ jobs:
echo "=== Verifying Build Artifacts for SonarQube ==="
echo ""

# Dynamically get modules from settings.gradle (extract module names from "include ':modulename'" lines)
MODULES=$(grep "^include" settings.gradle | cut -d"'" -f2 | cut -d":" -f2 | tr '\n' ' ')
echo "Detected modules: $MODULES"
echo ""

echo "Checking compiled class files for each module:"
for module in main events logger; do
for module in $MODULES; do
MODULE_CLASSES_DIR="${module}/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes"
if [ -d "$MODULE_CLASSES_DIR" ]; then
CLASS_COUNT=$(find "$MODULE_CLASSES_DIR" -name "*.class" | wc -l)
Expand Down Expand Up @@ -98,7 +108,7 @@ jobs:

echo ""
echo "Checking JaCoCo execution data for each module:"
for module in main events logger; do
for module in $MODULES; do
EXEC_FILE="${module}/build/jacoco/testDebugUnitTest.exec"
if [ -f "$EXEC_FILE" ]; then
EXEC_SIZE=$(wc -c < "$EXEC_FILE")
Expand Down Expand Up @@ -127,7 +137,7 @@ jobs:
echo "=== Verification Complete ==="

- name: SonarCloud Scan
uses: SonarSource/sonarqube-scan-action@v6
uses: SonarSource/sonarqube-scan-action@v7
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information
SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }}
Expand Down
1 change: 0 additions & 1 deletion api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@
This module contains the public API interfaces and types exposed to consumers of the Split SDK.

Classes in this module are part of the public API contract and should maintain backwards compatibility.

28 changes: 28 additions & 0 deletions api/src/main/java/io/split/android/client/SplitClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.Map;

import io.split.android.client.attributes.AttributesManager;
import io.split.android.client.events.SdkEventListener;
import io.split.android.client.events.SplitEvent;
import io.split.android.client.events.SplitEventTask;

Expand Down Expand Up @@ -179,6 +180,33 @@ public interface SplitClient extends AttributesManager {

void on(SplitEvent event, SplitEventTask task);

/**
* Registers an event listener for SDK events that provide typed metadata.
* <p>
* This method provides type-safe callbacks for SDK_UPDATE and SDK_READY_FROM_CACHE events.
* Override the methods you need in the listener.
* <p>
* Example usage:
* <pre>{@code
* client.addEventListener(new SdkEventListener() {
* @Override
* public void onUpdate(SplitClient client, SdkUpdateMetadata metadata) {
* List<String> flags = metadata.getUpdatedFlags();
* // Handle on background thread
* }
*
* @Override
* public void onReadyFromCacheView(SplitClient client, SdkReadyFromCacheMetadata metadata) {
* // Handle on main/UI thread
* Boolean freshInstall = metadata.isFreshInstall();
* }
* });
* }</pre>
*
* @param listener the event listener to register
*/
void addEventListener(SdkEventListener listener);

/**
* Enqueue a new event to be sent to Split data collection services.
* <p>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package io.split.android.client.events;

import io.split.android.client.SplitClient;

/**
* Abstract class for handling SDK events with typed metadata.
* <p>
* Extend this class and override the methods you need to handle specific SDK events.
* Each event has two callback options:
* <ul>
* <li>Background thread callbacks (e.g., {@link #onUpdate}) - executed immediately on a background thread</li>
* <li>Main thread callbacks (e.g., {@link #onUpdateView}) - executed on the main/UI thread</li>
* </ul>
* <p>
* Example usage:
* <pre>{@code
* client.addEventListener(new SdkEventListener() {
* @Override
* public void onUpdate(SplitClient client, SdkUpdateMetadata metadata) {
* List<String> flags = metadata.getUpdatedFlags();
* // Handle updated flags on background thread
* }
*
* @Override
* public void onReadyFromCacheView(SplitClient client, SdkReadyFromCacheMetadata metadata) {
* // Handle cache ready on main/UI thread
* Boolean freshInstall = metadata.isFreshInstall();
* }
* });
* }</pre>
*/
public abstract class SdkEventListener {

/**
* Called when SDK_UPDATE event occurs, executed on a background thread.
* <p>
* Override this method to handle SDK_UPDATE events with typed metadata.
*
* @param client the Split client instance
* @param metadata the typed metadata containing updated flag information
*/
public void onUpdate(SplitClient client, SdkUpdateMetadata metadata) {
// Default empty implementation
}

/**
* Called when SDK_READY_FROM_CACHE event occurs, executed on a background thread.
* <p>
* Override this method to handle SDK_READY_FROM_CACHE events with typed metadata.
*
* @param client the Split client instance
* @param metadata the typed metadata containing cache information
*/
public void onReadyFromCache(SplitClient client, SdkReadyFromCacheMetadata metadata) {
// Default empty implementation
}

/**
* Called when SDK_UPDATE event occurs, executed on the main/UI thread.
* <p>
* Override this method to handle SDK_UPDATE events with typed metadata on the main thread.
* Use this when you need to update UI components.
*
* @param client the Split client instance
* @param metadata the typed metadata containing updated flag information
*/
public void onUpdateView(SplitClient client, SdkUpdateMetadata metadata) {
// Default empty implementation
}

/**
* Called when SDK_READY_FROM_CACHE event occurs, executed on the main/UI thread.
* <p>
* Override this method to handle SDK_READY_FROM_CACHE events with typed metadata on the main thread.
* Use this when you need to update UI components.
*
* @param client the Split client instance
* @param metadata the typed metadata containing cache information
*/
public void onReadyFromCacheView(SplitClient client, SdkReadyFromCacheMetadata metadata) {
// Default empty implementation
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.split.android.client.events;

import androidx.annotation.Nullable;

/**
* Typed metadata for SDK_READY_FROM_CACHE events.
* <p>
* Contains information about the cache state when the SDK is ready from cache.
*/
public final class SdkReadyFromCacheMetadata {

@Nullable
private final Boolean mFreshInstall;

@Nullable
private final Long mLastUpdateTimestamp;

/**
* Creates a new SdkReadyFromCacheMetadata instance.
*
* @param freshInstall true if this is a fresh install with no usable cache, or null if not available
* @param lastUpdateTimestamp the last successful cache timestamp in milliseconds since epoch, or null if not available
*/
public SdkReadyFromCacheMetadata(@Nullable Boolean freshInstall, @Nullable Long lastUpdateTimestamp) {
mFreshInstall = freshInstall;
mLastUpdateTimestamp = lastUpdateTimestamp;
}

/**
* Returns whether this is a fresh install with no usable cache.
*
* @return true if fresh install, false otherwise, or null if not available
*/
@Nullable
public Boolean isFreshInstall() {
return mFreshInstall;
}

/**
* Returns the last successful cache timestamp in milliseconds since epoch.
*
* @return the timestamp, or null if not available
*/
@Nullable
public Long getLastUpdateTimestamp() {
return mLastUpdateTimestamp;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.split.android.client.events;

import androidx.annotation.Nullable;

import java.util.List;

/**
* Typed metadata for SDK_UPDATE events.
* <p>
* Contains information about flags that were updated in the event.
*/
public final class SdkUpdateMetadata {

@Nullable
private final List<String> mUpdatedFlags;

/**
* Creates a new SdkUpdateMetadata instance.
*
* @param updatedFlags the list of flag names that were updated, or null if not available
*/
public SdkUpdateMetadata(@Nullable List<String> updatedFlags) {
mUpdatedFlags = updatedFlags;
}

/**
* Returns the list of flag names that changed in this update.
*
* @return the list of updated flag names, or null if not available
*/
@Nullable
public List<String> getUpdatedFlags() {
return mUpdatedFlags;
}
}

Original file line number Diff line number Diff line change
@@ -1,43 +1,27 @@
package io.split.android.client.events;

import androidx.annotation.Nullable;

import io.split.android.client.SplitClient;
import io.split.android.client.api.EventMetadata;

/**
* Base class for handling Split SDK events.
* <p>
* Extend this class and override the methods you need to handle specific SDK events.
* You can implement both the metadata-enabled and versions of the methods;
* if both are implemented, both will be called (metadata version first).
* <p>
* <b>Threading:</b>
* <ul>
* <li>{@code onPostExecution} methods are called on a background thread (faster, executed immediately)</li>
* <li>{@code onPostExecutionView} methods are called on the main/UI thread (queued on main looper)</li>
* </ul>
* <p>
* <b>Metadata:</b>
* <ul>
* <li>Metadata-enabled methods receive {@link EventMetadata} containing event-specific information</li>
* <li>Metadata may be {@code null} for some events</li>
* <li>If you only need metadata, implement the metadata version; if you need backward compatibility,
* implement both versions</li>
* </ul>
* For events with metadata (like SDK_UPDATE or SDK_READY_FROM_CACHE), use
* {@link SdkEventListener} instead for type-safe metadata access.
* <p>
* Example usage:
* <pre>{@code
* client.on(SplitEvent.SDK_UPDATE, new SplitEventTask() {
* @Override
* public void onPostExecution(SplitClient client, EventMetadata metadata) {
* List<String> updatedFlags = (List<String>) metadata.get("updatedFlags");
* // Handle update with metadata
* }
*
* client.on(SplitEvent.SDK_READY, new SplitEventTask() {
* @Override
* public void onPostExecution(SplitClient client) {
* // Legacy handling (also called if both are implemented)
* // SDK is ready, start using Split
* }
* });
* }</pre>
Expand All @@ -46,7 +30,7 @@ public class SplitEventTask {
/**
* Called when an event occurs, executed on a background thread.
* <p>
* Override this method to handle events on a background thread without metadata.
* Override this method to handle events on a background thread.
* This method is executed immediately and is faster than {@link #onPostExecutionView(SplitClient)}.
*
* @param client the Split client instance
Expand All @@ -59,7 +43,7 @@ public void onPostExecution(SplitClient client) {
/**
* Called when an event occurs, executed on the main/UI thread.
* <p>
* Override this method to handle events on the main thread without metadata.
* Override this method to handle events on the main thread.
* Use this when you need to update UI components.
* <p>
* Note: This method is queued on the main looper, so execution may be delayed
Expand All @@ -71,43 +55,4 @@ public void onPostExecution(SplitClient client) {
public void onPostExecutionView(SplitClient client) {
throw new SplitEventTaskMethodNotImplementedException();
}

/**
* Called when an event occurs with metadata, executed on a background thread.
* <p>
* Override this method to handle events on a background thread with access to event metadata.
* The metadata contains event-specific information such as updated flag names for SDK_UPDATE events.
* This method is executed immediately and is faster than {@link #onPostExecutionView(SplitClient, EventMetadata)}.
* <p>
* If both this method and {@link #onPostExecution(SplitClient)} are implemented,
* both will be called (this method first).
*
* @param client the Split client instance
* @param metadata the event metadata, may be {@code null} for some events
* @throws SplitEventTaskMethodNotImplementedException if not overridden (default behavior)
*/
public void onPostExecution(SplitClient client, @Nullable EventMetadata metadata) {
throw new SplitEventTaskMethodNotImplementedException();
}

/**
* Called when an event occurs with metadata, executed on the main/UI thread.
* <p>
* Override this method to handle events on the main thread with access to event metadata.
* The metadata contains event-specific information such as updated flag names for SDK_UPDATE events.
* Use this when you need to update UI components based on event metadata.
* <p>
* Note: This method is queued on the main looper, so execution may be delayed
* compared to {@link #onPostExecution(SplitClient, EventMetadata)}.
* <p>
* If both this method and {@link #onPostExecutionView(SplitClient)} are implemented,
* both will be called (this method first).
*
* @param client the Split client instance
* @param metadata the event metadata, may be {@code null} for some events
* @throws SplitEventTaskMethodNotImplementedException if not overridden (default behavior)
*/
public void onPostExecutionView(SplitClient client, @Nullable EventMetadata metadata) {
throw new SplitEventTaskMethodNotImplementedException();
}
}
Loading
Loading