diff --git a/build.gradle b/build.gradle
index 5cd9478e0..20adf0399 100644
--- a/build.gradle
+++ b/build.gradle
@@ -145,7 +145,7 @@ dependencies {
return candidates.find { findProject(it) != null }
}
- ['main', 'logger', 'events', 'events-domain', 'api', 'http-api', 'http'].each { moduleName ->
+ ['main', 'logger', 'events', 'events-domain', 'api', 'http-api', 'http', 'fallback'].each { moduleName ->
def resolvedPath = resolveProjectPath(moduleName)
if (resolvedPath != null) {
include project(resolvedPath)
diff --git a/fallback/.gitignore b/fallback/.gitignore
new file mode 100644
index 000000000..3a11ced48
--- /dev/null
+++ b/fallback/.gitignore
@@ -0,0 +1,4 @@
+/build
+.gradle
+*.iml
+.DS_Store
diff --git a/fallback/README.md b/fallback/README.md
new file mode 100644
index 000000000..b9bad4614
--- /dev/null
+++ b/fallback/README.md
@@ -0,0 +1,10 @@
+# Fallback module
+
+This module contains the fallback treatment logic for the Split SDK.
+
+It provides the types and resolution strategy used when the SDK is unable to evaluate a feature flag normally (e.g. during initialization or on error), returning a configured fallback treatment instead of the default "control".
+
+Key types:
+- `FallbackTreatmentsConfiguration` — builder for configuring global and per-flag fallbacks
+- `FallbackTreatment` — represents a fallback treatment value with optional config and label
+- `FallbackTreatmentsCalculator` — resolves the applicable fallback for a given flag name
diff --git a/fallback/build.gradle b/fallback/build.gradle
new file mode 100644
index 000000000..fc30c3b42
--- /dev/null
+++ b/fallback/build.gradle
@@ -0,0 +1,23 @@
+plugins {
+ id 'com.android.library'
+}
+
+apply from: "$projectDir/../gradle/common-android-library.gradle"
+
+android {
+ namespace 'io.split.android.client.fallback'
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+ api project(':logger')
+
+ implementation libs.annotation
+
+ testImplementation libs.junit4
+ testImplementation libs.mockitoCore
+}
diff --git a/fallback/src/main/AndroidManifest.xml b/fallback/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..9a40236b9
--- /dev/null
+++ b/fallback/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/main/src/main/java/io/split/android/client/fallback/FallbackTreatment.java b/fallback/src/main/java/io/split/android/client/fallback/FallbackTreatment.java
similarity index 100%
rename from main/src/main/java/io/split/android/client/fallback/FallbackTreatment.java
rename to fallback/src/main/java/io/split/android/client/fallback/FallbackTreatment.java
diff --git a/main/src/main/java/io/split/android/client/fallback/FallbackTreatmentsCalculator.java b/fallback/src/main/java/io/split/android/client/fallback/FallbackTreatmentsCalculator.java
similarity index 100%
rename from main/src/main/java/io/split/android/client/fallback/FallbackTreatmentsCalculator.java
rename to fallback/src/main/java/io/split/android/client/fallback/FallbackTreatmentsCalculator.java
diff --git a/main/src/main/java/io/split/android/client/fallback/FallbackTreatmentsCalculatorImpl.java b/fallback/src/main/java/io/split/android/client/fallback/FallbackTreatmentsCalculatorImpl.java
similarity index 86%
rename from main/src/main/java/io/split/android/client/fallback/FallbackTreatmentsCalculatorImpl.java
rename to fallback/src/main/java/io/split/android/client/fallback/FallbackTreatmentsCalculatorImpl.java
index 0eb727a1e..d67a7c084 100644
--- a/main/src/main/java/io/split/android/client/fallback/FallbackTreatmentsCalculatorImpl.java
+++ b/fallback/src/main/java/io/split/android/client/fallback/FallbackTreatmentsCalculatorImpl.java
@@ -1,23 +1,21 @@
package io.split.android.client.fallback;
-import static io.split.android.client.utils.Utils.checkNotNull;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Map;
-
-import io.split.android.grammar.Treatments;
+import java.util.Objects;
public final class FallbackTreatmentsCalculatorImpl implements FallbackTreatmentsCalculator {
private static final String LABEL_PREFIX = "fallback - ";
+ private static final String CONTROL = "control";
@NonNull
private final FallbackTreatmentsConfiguration mConfig;
public FallbackTreatmentsCalculatorImpl(@NonNull FallbackTreatmentsConfiguration config) {
- mConfig = checkNotNull(config);
+ mConfig = Objects.requireNonNull(config);
}
@NonNull
@@ -40,7 +38,7 @@ public FallbackTreatment resolve(@NonNull String flagName, @Nullable String labe
if (global != null) {
return global.copyWithLabel(resolveLabel(label));
}
- return new FallbackTreatment(Treatments.CONTROL, null, label);
+ return new FallbackTreatment(CONTROL, null, label);
}
@Nullable
diff --git a/main/src/main/java/io/split/android/client/fallback/FallbackTreatmentsConfiguration.java b/fallback/src/main/java/io/split/android/client/fallback/FallbackTreatmentsConfiguration.java
similarity index 100%
rename from main/src/main/java/io/split/android/client/fallback/FallbackTreatmentsConfiguration.java
rename to fallback/src/main/java/io/split/android/client/fallback/FallbackTreatmentsConfiguration.java
diff --git a/main/src/main/java/io/split/android/client/fallback/FallbacksSanitizer.java b/fallback/src/main/java/io/split/android/client/fallback/FallbacksSanitizer.java
similarity index 100%
rename from main/src/main/java/io/split/android/client/fallback/FallbacksSanitizer.java
rename to fallback/src/main/java/io/split/android/client/fallback/FallbacksSanitizer.java
diff --git a/main/src/main/java/io/split/android/client/fallback/FallbacksSanitizerImpl.java b/fallback/src/main/java/io/split/android/client/fallback/FallbacksSanitizerImpl.java
similarity index 100%
rename from main/src/main/java/io/split/android/client/fallback/FallbacksSanitizerImpl.java
rename to fallback/src/main/java/io/split/android/client/fallback/FallbacksSanitizerImpl.java
diff --git a/main/src/test/java/io/split/android/client/fallback/FallbackTreatmentTest.java b/fallback/src/test/java/io/split/android/client/fallback/FallbackTreatmentTest.java
similarity index 100%
rename from main/src/test/java/io/split/android/client/fallback/FallbackTreatmentTest.java
rename to fallback/src/test/java/io/split/android/client/fallback/FallbackTreatmentTest.java
diff --git a/main/src/test/java/io/split/android/client/fallback/FallbackTreatmentsCalculatorTest.java b/fallback/src/test/java/io/split/android/client/fallback/FallbackTreatmentsCalculatorTest.java
similarity index 98%
rename from main/src/test/java/io/split/android/client/fallback/FallbackTreatmentsCalculatorTest.java
rename to fallback/src/test/java/io/split/android/client/fallback/FallbackTreatmentsCalculatorTest.java
index 37c5353c0..c122f4257 100644
--- a/main/src/test/java/io/split/android/client/fallback/FallbackTreatmentsCalculatorTest.java
+++ b/fallback/src/test/java/io/split/android/client/fallback/FallbackTreatmentsCalculatorTest.java
@@ -10,8 +10,6 @@
import java.util.HashMap;
import java.util.Map;
-import io.split.android.client.TreatmentLabels;
-
public class FallbackTreatmentsCalculatorTest {
@Test
@@ -103,7 +101,7 @@ public void labelIsPrefixed() {
.build();
FallbackTreatmentsCalculator calculator = new FallbackTreatmentsCalculatorImpl(config);
- FallbackTreatment resolved = calculator.resolve("flagA", TreatmentLabels.EXCEPTION);
+ FallbackTreatment resolved = calculator.resolve("flagA", "exception");
assertNotNull(resolved);
assertEquals("fallback - exception", resolved.getLabel());
diff --git a/main/src/test/java/io/split/android/client/fallback/FallbackTreatmentsConfigurationTest.java b/fallback/src/test/java/io/split/android/client/fallback/FallbackTreatmentsConfigurationTest.java
similarity index 100%
rename from main/src/test/java/io/split/android/client/fallback/FallbackTreatmentsConfigurationTest.java
rename to fallback/src/test/java/io/split/android/client/fallback/FallbackTreatmentsConfigurationTest.java
diff --git a/main/src/test/java/io/split/android/client/fallback/FallbacksSanitizerImplTest.java b/fallback/src/test/java/io/split/android/client/fallback/FallbacksSanitizerImplTest.java
similarity index 100%
rename from main/src/test/java/io/split/android/client/fallback/FallbacksSanitizerImplTest.java
rename to fallback/src/test/java/io/split/android/client/fallback/FallbacksSanitizerImplTest.java
diff --git a/fallback/src/test/java/io/split/android/client/utils/logger/LogPrinterStub.java b/fallback/src/test/java/io/split/android/client/utils/logger/LogPrinterStub.java
new file mode 100644
index 000000000..e8c53ef3d
--- /dev/null
+++ b/fallback/src/test/java/io/split/android/client/utils/logger/LogPrinterStub.java
@@ -0,0 +1,66 @@
+package io.split.android.client.utils.logger;
+
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+public class LogPrinterStub implements LogPrinter {
+ private final Set calls = new HashSet<>();
+ private final Map> logs = new ConcurrentHashMap<>();
+
+ public LogPrinterStub() {
+ // Initialize for all Android log levels: VERBOSE(2) .. ASSERT(7)
+ for (int level = Log.VERBOSE; level <= Log.ASSERT; level++) {
+ logs.put(level, new ConcurrentLinkedDeque<>());
+ }
+ }
+
+ @Override
+ public void v(String tag, String msg, Throwable tr) {
+ logs.get(Log.VERBOSE).add(msg);
+ calls.add(Log.VERBOSE);
+ }
+
+ @Override
+ public void d(String tag, String msg, Throwable tr) {
+ logs.get(Log.DEBUG).add(msg);
+ calls.add(Log.DEBUG);
+ }
+
+ @Override
+ public void i(String tag, String msg, Throwable tr) {
+ logs.get(Log.INFO).add(msg);
+ calls.add(Log.INFO);
+ }
+
+ @Override
+ public void w(String tag, String msg, Throwable tr) {
+ logs.get(Log.WARN).add(msg);
+ calls.add(Log.WARN);
+ }
+
+ @Override
+ public void e(String tag, String msg, Throwable tr) {
+ logs.get(Log.ERROR).add(msg);
+ calls.add(Log.ERROR);
+ }
+
+ @Override
+ public void wtf(String tag, String msg, Throwable tr) {
+ logs.get(Log.ASSERT).add(msg);
+ calls.add(Log.ASSERT);
+ }
+
+ public boolean isCalled(Integer type) {
+ return calls.contains(type);
+ }
+
+ public Map> getLoggedMessages() {
+ return new HashMap<>(logs);
+ }
+}
diff --git a/main/build.gradle b/main/build.gradle
index 362b7508f..d79d1518f 100644
--- a/main/build.gradle
+++ b/main/build.gradle
@@ -53,6 +53,7 @@ dependencies {
api project(':logger')
api project(':api')
api project(':http-api')
+ api project(':fallback')
// Internal module dependencies
implementation project(':http')
implementation project(':events-domain')
diff --git a/settings.gradle b/settings.gradle
index 9ddd50403..ecfd8fb57 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -4,6 +4,7 @@ include ':api'
include ':logger'
include ':http-api'
include ':http'
+include ':fallback'
include ':main'
include ':events'
include ':events-domain'