From a6b8ef569cdbf28ce2806fdcbe1114476a874f3c Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 16 Dec 2025 16:37:30 +0300 Subject: [PATCH 01/14] feat: list filter utils --- .../android/sdk/UtilsListingFilters.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 sdk/src/main/java/ly/count/android/sdk/UtilsListingFilters.java diff --git a/sdk/src/main/java/ly/count/android/sdk/UtilsListingFilters.java b/sdk/src/main/java/ly/count/android/sdk/UtilsListingFilters.java new file mode 100644 index 000000000..985a7acf2 --- /dev/null +++ b/sdk/src/main/java/ly/count/android/sdk/UtilsListingFilters.java @@ -0,0 +1,71 @@ +package ly.count.android.sdk; + +import androidx.annotation.NonNull; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +public class UtilsListingFilters { + + private UtilsListingFilters() { + } + + static boolean applyEventFilter(@NonNull String eventName, @NonNull ConfigurationProvider configProvider) { + return applyListFilter(eventName, configProvider.getEventFilterSet(), configProvider.getFilterIsWhitelist()); + } + + static boolean applyUserPropertyFilter(@NonNull String propertyName, @NonNull ConfigurationProvider configProvider) { + return applyListFilter(propertyName, configProvider.getUserPropertyFilterSet(), configProvider.getFilterIsWhitelist()); + } + + static void applySegmentationFilter(@NonNull Map segmentation, @NonNull ConfigurationProvider configProvider, @NonNull ModuleLog L) { + if (segmentation.isEmpty()) { + return; + } + applyMapFilter(segmentation, configProvider.getSegmentationFilterSet(), configProvider.getFilterIsWhitelist(), L); + } + + static void applyEventSegmentationFilter(@NonNull String eventName, @NonNull Map segmentation, + @NonNull ConfigurationProvider configProvider, @NonNull ModuleLog L) { + if (segmentation.isEmpty() || configProvider.getEventSegmentationFilterMap().isEmpty()) { + return; + } + + Set segmentationSet = configProvider.getEventSegmentationFilterMap().get(eventName); + if (segmentationSet == null || segmentationSet.isEmpty()) { + // No rules defined for this event so allow everything + return; + } + applyMapFilter(segmentation, segmentationSet, configProvider.getFilterIsWhitelist(), L); + } + + private static void applyMapFilter(@NonNull Map map, @NonNull Set filterSet, boolean isWhitelist, @NonNull ModuleLog L) { + if (filterSet.isEmpty()) { + // No rules defined so allow everything + return; + } + + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + String key = entry.getKey(); + + boolean contains = filterSet.contains(key); + + // Whitelist: remove if NOT in list + // Blacklist: remove if IN list + if ((isWhitelist && !contains) || (!isWhitelist && contains)) { + iterator.remove(); + L.d("[UtilsListingFilters] applyMapFilter, removed key: " + key + (isWhitelist ? "not in whitelist" : "blacklisted")); + } + } + } + + private static boolean applyListFilter(String item, @NonNull Set filterSet, boolean isWhitelist) { + if (filterSet.isEmpty()) { + // No rules defined so allow everything + return true; + } + return isWhitelist == filterSet.contains(item); + } +} From 59b62cddd73b482a2addcc21762a52117467079f Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 16 Dec 2025 16:37:49 +0300 Subject: [PATCH 02/14] feat: add methods to the config provider --- .../count/android/sdk/ConfigurationProvider.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sdk/src/main/java/ly/count/android/sdk/ConfigurationProvider.java b/sdk/src/main/java/ly/count/android/sdk/ConfigurationProvider.java index ec42ae165..ed8b2de05 100644 --- a/sdk/src/main/java/ly/count/android/sdk/ConfigurationProvider.java +++ b/sdk/src/main/java/ly/count/android/sdk/ConfigurationProvider.java @@ -1,5 +1,8 @@ package ly.count.android.sdk; +import java.util.Map; +import java.util.Set; + interface ConfigurationProvider { boolean getNetworkingEnabled(); @@ -31,4 +34,17 @@ interface ConfigurationProvider { int getBOMDuration(); int getRequestTimeoutDurationMillis(); + + int getUserPropertyCacheLimit(); + + // LISTING FILTERS + boolean getFilterIsWhitelist(); + + Set getEventFilterSet(); + + Set getUserPropertyFilterSet(); + + Set getSegmentationFilterSet(); + + Map> getEventSegmentationFilterMap(); } From f8cb6841f7b89ad0765390434ea2e0357b42ae84 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 16 Dec 2025 16:38:20 +0300 Subject: [PATCH 03/14] feat: add new params to the tests --- .../android/sdk/ConnectionProcessorTests.java | 27 +++++++++++++++++++ .../android/sdk/ModuleConfigurationTests.java | 11 +++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/ConnectionProcessorTests.java b/sdk/src/androidTest/java/ly/count/android/sdk/ConnectionProcessorTests.java index 03cf9137e..17a3be946 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/ConnectionProcessorTests.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/ConnectionProcessorTests.java @@ -30,7 +30,10 @@ of this software and associated documentation files (the "Software"), to deal import java.net.URL; import java.net.URLConnection; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -130,6 +133,30 @@ public void setUp() { @Override public int getRequestTimeoutDurationMillis() { return 30_000; } + + @Override public int getUserPropertyCacheLimit() { + return 100; + } + + @Override public boolean getFilterIsWhitelist() { + return false; + } + + @Override public Set getEventFilterSet() { + return new HashSet<>(); + } + + @Override public Set getUserPropertyFilterSet() { + return new HashSet<>(); + } + + @Override public Set getSegmentationFilterSet() { + return new HashSet<>(); + } + + @Override public Map> getEventSegmentationFilterMap() { + return new ConcurrentHashMap<>(); + } }; Countly.sharedInstance().setLoggingEnabled(true); diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java b/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java index 625d43649..1ff135e10 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java @@ -4,6 +4,7 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -1123,7 +1124,15 @@ private void initServerConfigWithValues(BiConsumer config .segmentationValuesLimit(25) .breadcrumbLimit(90) .traceLengthLimit(78) - .traceLinesLimit(89); + .traceLinesLimit(89) + .userPropertyCacheLimit(67) + + // Filters + .filterPreset("Whitelisting") + .eventFilterList(new HashSet<>()) + .userPropertyFilterList(new HashSet<>()) + .segmentationFilterList(new HashSet<>()) + .eventSegmentationFilterMap(new ConcurrentHashMap<>()); String serverConfig = builder.build(); CountlyConfig countlyConfig = TestUtils.createBaseConfig().setLoggingEnabled(false); From f96c8cb4dee6167efd64b1b18468126e975a4c50 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 16 Dec 2025 16:38:34 +0300 Subject: [PATCH 04/14] feat: update test builder --- .../android/sdk/ServerConfigBuilder.java | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java b/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java index 003c68726..458bd0138 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java @@ -2,6 +2,9 @@ import java.util.HashMap; import java.util.Map; +import java.util.Objects; +import java.util.Set; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.junit.Assert; @@ -13,7 +16,10 @@ import static ly.count.android.sdk.ModuleConfiguration.keyRCustomEventTracking; import static ly.count.android.sdk.ModuleConfiguration.keyRDropOldRequestTime; import static ly.count.android.sdk.ModuleConfiguration.keyREnterContentZone; +import static ly.count.android.sdk.ModuleConfiguration.keyREventFilterList; import static ly.count.android.sdk.ModuleConfiguration.keyREventQueueSize; +import static ly.count.android.sdk.ModuleConfiguration.keyREventSegmentationFilterList; +import static ly.count.android.sdk.ModuleConfiguration.keyRFilterPreset; import static ly.count.android.sdk.ModuleConfiguration.keyRLimitBreadcrumb; import static ly.count.android.sdk.ModuleConfiguration.keyRLimitKeyLength; import static ly.count.android.sdk.ModuleConfiguration.keyRLimitSegValues; @@ -25,11 +31,14 @@ import static ly.count.android.sdk.ModuleConfiguration.keyRNetworking; import static ly.count.android.sdk.ModuleConfiguration.keyRRefreshContentZone; import static ly.count.android.sdk.ModuleConfiguration.keyRReqQueueSize; +import static ly.count.android.sdk.ModuleConfiguration.keyRSegmentationFilterList; import static ly.count.android.sdk.ModuleConfiguration.keyRServerConfigUpdateInterval; import static ly.count.android.sdk.ModuleConfiguration.keyRSessionTracking; import static ly.count.android.sdk.ModuleConfiguration.keyRSessionUpdateInterval; import static ly.count.android.sdk.ModuleConfiguration.keyRTimestamp; import static ly.count.android.sdk.ModuleConfiguration.keyRTracking; +import static ly.count.android.sdk.ModuleConfiguration.keyRUserPropertyCacheLimit; +import static ly.count.android.sdk.ModuleConfiguration.keyRUserPropertyFilterList; import static ly.count.android.sdk.ModuleConfiguration.keyRVersion; import static ly.count.android.sdk.ModuleConfiguration.keyRViewTracking; @@ -169,6 +178,36 @@ ServerConfigBuilder traceLinesLimit(int limit) { return this; } + ServerConfigBuilder userPropertyCacheLimit(int limit) { + config.put(keyRUserPropertyCacheLimit, limit); + return this; + } + + ServerConfigBuilder filterPreset(String preset) { + config.put(keyRFilterPreset, preset); + return this; + } + + ServerConfigBuilder eventFilterList(Set filterList) { + config.put(keyREventFilterList, filterList); + return this; + } + + ServerConfigBuilder userPropertyFilterList(Set filterList) { + config.put(keyRUserPropertyFilterList, filterList); + return this; + } + + ServerConfigBuilder segmentationFilterList(Set filterList) { + config.put(keyRSegmentationFilterList, filterList); + return this; + } + + ServerConfigBuilder eventSegmentationFilterMap(Map> filterMap) { + config.put(keyREventSegmentationFilterList, filterMap); + return this; + } + ServerConfigBuilder defaults() { // Feature flags tracking(true); @@ -198,6 +237,13 @@ ServerConfigBuilder defaults() { breadcrumbLimit(Countly.maxBreadcrumbCountDefault); traceLengthLimit(Countly.maxStackTraceLineLengthDefault); traceLinesLimit(Countly.maxStackTraceLinesPerThreadDefault); + userPropertyCacheLimit(100); + + filterPreset("Blacklisting"); + eventFilterList(new JSONArray()); + userPropertyFilterList(new JSONArray()); + segmentationFilterList(new JSONArray()); + eventSegmentationFilterMap(new JSONObject()); return this; } @@ -221,6 +267,7 @@ void validateAgainst(Countly countly) { validateFeatureFlags(countly); validateIntervalsAndSizes(countly); validateLimits(countly); + validateFilterSettings(countly); } private void validateFeatureFlags(Countly countly) { @@ -260,5 +307,22 @@ private void validateLimits(Countly countly) { Assert.assertEquals(config.get(keyRLimitBreadcrumb), countly.config_.sdkInternalLimits.maxBreadcrumbCount); Assert.assertEquals(config.get(keyRLimitTraceLength), countly.config_.sdkInternalLimits.maxStackTraceLineLength); Assert.assertEquals(config.get(keyRLimitTraceLine), countly.config_.sdkInternalLimits.maxStackTraceLinesPerThread); + Assert.assertEquals(config.get(keyRUserPropertyCacheLimit), countly.moduleConfiguration.getUserPropertyCacheLimit()); + } + + private void validateFilterSettings(Countly countly) { + Assert.assertEquals(config.get(keyRFilterPreset), countly.moduleConfiguration.currentVFilterPreset); + + JSONArray eventFilterList = (JSONArray) config.get(keyREventFilterList); + Assert.assertEquals(Objects.requireNonNull(eventFilterList).toString(), countly.moduleConfiguration.getEventFilterSet().toString()); + + JSONArray userPropertyFilterList = (JSONArray) config.get(keyRUserPropertyFilterList); + Assert.assertEquals(Objects.requireNonNull(userPropertyFilterList).toString(), countly.moduleConfiguration.getUserPropertyFilterSet().toString()); + + JSONArray segmentationFilterList = (JSONArray) config.get(keyRSegmentationFilterList); + Assert.assertEquals(Objects.requireNonNull(segmentationFilterList).toString(), countly.moduleConfiguration.getSegmentationFilterSet().toString()); + + JSONObject eventSegmentationFilterMap = (JSONObject) config.get(keyREventSegmentationFilterList); + Assert.assertEquals(Objects.requireNonNull(eventSegmentationFilterMap).toString(), countly.moduleConfiguration.getEventSegmentationFilterMap().toString()); } } \ No newline at end of file From 07281afc743d43ed3cc1942558ad0b27147c1553 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 16 Dec 2025 16:39:07 +0300 Subject: [PATCH 05/14] feat: integrate new features to the module --- .../android/sdk/ModuleConfiguration.java | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/sdk/src/main/java/ly/count/android/sdk/ModuleConfiguration.java b/sdk/src/main/java/ly/count/android/sdk/ModuleConfiguration.java index df7abc8cc..4c50e0488 100644 --- a/sdk/src/main/java/ly/count/android/sdk/ModuleConfiguration.java +++ b/sdk/src/main/java/ly/count/android/sdk/ModuleConfiguration.java @@ -2,7 +2,12 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import java.util.HashSet; import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -47,6 +52,12 @@ class ModuleConfiguration extends ModuleBase implements ConfigurationProvider { final static String keyRBOMRQPercentage = "bom_rqp"; final static String keyRBOMRequestAge = "bom_ra"; final static String keyRBOMDuration = "bom_d"; + final static String keyRUserPropertyCacheLimit = "upcl"; + final static String keyRFilterPreset = "filter_preset"; + final static String keyREventFilterList = "eb"; + final static String keyRUserPropertyFilterList = "upb"; + final static String keyRSegmentationFilterList = "sb"; + final static String keyREventSegmentationFilterList = "esb"; // json // FLAGS boolean currentVTracking = true; boolean currentVNetworking = true; @@ -64,6 +75,14 @@ class ModuleConfiguration extends ModuleBase implements ConfigurationProvider { double currentVBOMRQPercentage = 0.5; int currentVBOMRequestAge = 24; // in hours int currentVBOMDuration = 60; // in seconds + int currentVUserPropertyCacheLimit = 100; + + // FILTERS + String currentVFilterPreset = "Blacklisting"; + Set currentVEventFilterList = new HashSet<>(); + Set currentVUserPropertyFilterList = new HashSet<>(); + Set currentVSegmentationFilterList = new HashSet<>(); + Map> currentVEventSegmentationFilterList = new ConcurrentHashMap<>(); // SERVER CONFIGURATION PARAMS Integer serverConfigUpdateInterval; // in hours @@ -207,6 +226,8 @@ private void updateConfigVariables(@NonNull final CountlyConfig clyConfig) { currentVBOMRQPercentage = extractValue(keyRBOMRQPercentage, sb, currentVBOMRQPercentage, currentVBOMRQPercentage, Double.class, (Double value) -> value > 0.0 && value < 1.0); currentVBOMRequestAge = extractValue(keyRBOMRequestAge, sb, currentVBOMRequestAge, currentVBOMRequestAge, Integer.class, (Integer value) -> value > 0); currentVBOMDuration = extractValue(keyRBOMDuration, sb, currentVBOMDuration, currentVBOMDuration, Integer.class, (Integer value) -> value > 0); + currentVUserPropertyCacheLimit = extractValue(keyRUserPropertyCacheLimit, sb, currentVUserPropertyCacheLimit, currentVUserPropertyCacheLimit, Integer.class, (Integer value) -> value > 0); + currentVFilterPreset = extractValue(keyRFilterPreset, sb, currentVFilterPreset, currentVFilterPreset, String.class, (String value) -> value.equals("Blacklisting") || value.equals("Whitelisting")); clyConfig.setMaxRequestQueueSize(extractValue(keyRReqQueueSize, sb, clyConfig.maxRequestQueueSize, clyConfig.maxRequestQueueSize, Integer.class, (Integer value) -> value > 0)); clyConfig.setEventQueueSizeToSend(extractValue(keyREventQueueSize, sb, clyConfig.eventQueueSizeThreshold, Countly.sharedInstance().EVENT_QUEUE_SIZE_THRESHOLD, Integer.class, (Integer value) -> value > 0)); @@ -222,6 +243,8 @@ private void updateConfigVariables(@NonNull final CountlyConfig clyConfig) { clyConfig.setRequiresConsent(extractValue(keyRConsentRequired, sb, clyConfig.shouldRequireConsent, clyConfig.shouldRequireConsent)); clyConfig.setRequestDropAgeHours(extractValue(keyRDropOldRequestTime, sb, clyConfig.dropAgeHours, clyConfig.dropAgeHours, Integer.class, (Integer value) -> value >= 0)); + updateListingFilters(); + String updatedValues = sb.toString(); if (!updatedValues.isEmpty()) { L.i("[ModuleConfiguration] updateConfigVariables, SDK configuration has changed, notifying the SDK, new values: [" + updatedValues + "]"); @@ -229,6 +252,43 @@ private void updateConfigVariables(@NonNull final CountlyConfig clyConfig) { } } + private void updateListingFilters() { + JSONArray eventFilterListJSARR = latestRetrievedConfiguration.optJSONArray(keyREventFilterList); + JSONArray userPropertyFilterListJSARR = latestRetrievedConfiguration.optJSONArray(keyRUserPropertyFilterList); + JSONArray segmentationFilterListJSARR = latestRetrievedConfiguration.optJSONArray(keyRSegmentationFilterList); + JSONObject eventSegmentationFilterListJSOBJ = latestRetrievedConfiguration.optJSONObject(keyREventSegmentationFilterList); + + extractFilterSetFromJSONArray(eventFilterListJSARR, currentVEventFilterList); + extractFilterSetFromJSONArray(userPropertyFilterListJSARR, currentVUserPropertyFilterList); + extractFilterSetFromJSONArray(segmentationFilterListJSARR, currentVSegmentationFilterList); + if (eventSegmentationFilterListJSOBJ != null) { + currentVEventSegmentationFilterList.clear(); + Iterator keys = eventSegmentationFilterListJSOBJ.keys(); + while (keys.hasNext()) { + String key = keys.next(); + JSONArray jsonArray = eventSegmentationFilterListJSOBJ.optJSONArray(key); + if (jsonArray != null) { + Set filterSet = new HashSet<>(); + extractFilterSetFromJSONArray(jsonArray, filterSet); + currentVEventSegmentationFilterList.put(key, filterSet); + } + } + } + } + + private void extractFilterSetFromJSONArray(@Nullable JSONArray jsonArray, @NonNull Set targetSet) { + if (jsonArray == null) { + return; + } + targetSet.clear(); + for (int i = 0; i < jsonArray.length(); i++) { + String item = jsonArray.optString(i, null); + if (item != null) { + targetSet.add(item); + } + } + } + boolean validateServerConfig(@NonNull JSONObject config) { JSONObject newInner = config.optJSONObject(keyRConfig); @@ -293,6 +353,7 @@ private void removeUnsupportedKeys(@NonNull JSONObject newInner) { case keyRLimitBreadcrumb: case keyRLimitTraceLine: case keyRLimitTraceLength: + case keyRUserPropertyCacheLimit: isValid = value instanceof Integer && ((Integer) value) > 0; break; @@ -310,6 +371,19 @@ private void removeUnsupportedKeys(@NonNull JSONObject newInner) { case keyRBOMRQPercentage: isValid = value instanceof Double && ((Double) value > 0.0 && (Double) value < 1.0); break; + + // --- Filtering keys --- + case keyRFilterPreset: + isValid = value instanceof String && (value.equals("Blacklisting") || value.equals("Whitelisting")); + break; + case keyREventFilterList: + case keyRUserPropertyFilterList: + case keyRSegmentationFilterList: + isValid = value instanceof JSONArray; + break; + case keyREventSegmentationFilterList: + isValid = value instanceof JSONObject; + break; // --- Unknown keys --- default: L.w("[ModuleConfiguration] removeUnsupportedKeys, Unknown key: [" + key + "], removing it. value: [" + value + "]"); @@ -498,4 +572,28 @@ public boolean getTrackingEnabled() { @Override public int getRequestTimeoutDurationMillis() { return _cly.config_.requestTimeoutDuration * 1000; } + + @Override public int getUserPropertyCacheLimit() { + return currentVUserPropertyCacheLimit; + } + + @Override public boolean getFilterIsWhitelist() { + return currentVFilterPreset.equals("Whitelisting"); + } + + @Override public Set getEventFilterSet() { + return currentVEventFilterList; + } + + @Override public Set getUserPropertyFilterSet() { + return currentVUserPropertyFilterList; + } + + @Override public Set getSegmentationFilterSet() { + return currentVSegmentationFilterList; + } + + @Override public Map> getEventSegmentationFilterMap() { + return currentVEventSegmentationFilterList; + } } From 65e0fc84ca835d15563de9047768a8618e842311 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 16 Dec 2025 16:45:09 +0300 Subject: [PATCH 06/14] fix: test fixes --- .../android/sdk/ModuleConfigurationTests.java | 2 +- .../android/sdk/ServerConfigBuilder.java | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java b/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java index 1ff135e10..e3b3917ce 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java @@ -645,7 +645,7 @@ public void invalidConfigResponses_AreRejected() { */ @Test public void configurationParameterCount() { - int configParameterCount = 31; // plus config, timestamp and version parameters + int configParameterCount = 37; // plus config, timestamp and version parameters, UPDATE: list filters, and user property cache limit int count = 0; for (Field field : ModuleConfiguration.class.getDeclaredFields()) { if (field.getName().startsWith("keyR")) { diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java b/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java index 458bd0138..a5ea17e9f 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java @@ -1,10 +1,11 @@ package ly.count.android.sdk; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; -import org.json.JSONArray; +import java.util.concurrent.ConcurrentHashMap; import org.json.JSONException; import org.json.JSONObject; import org.junit.Assert; @@ -240,10 +241,10 @@ ServerConfigBuilder defaults() { userPropertyCacheLimit(100); filterPreset("Blacklisting"); - eventFilterList(new JSONArray()); - userPropertyFilterList(new JSONArray()); - segmentationFilterList(new JSONArray()); - eventSegmentationFilterMap(new JSONObject()); + eventFilterList(new HashSet<>()); + userPropertyFilterList(new HashSet<>()); + segmentationFilterList(new HashSet<>()); + eventSegmentationFilterMap(new ConcurrentHashMap<>()); return this; } @@ -313,16 +314,16 @@ private void validateLimits(Countly countly) { private void validateFilterSettings(Countly countly) { Assert.assertEquals(config.get(keyRFilterPreset), countly.moduleConfiguration.currentVFilterPreset); - JSONArray eventFilterList = (JSONArray) config.get(keyREventFilterList); + Set eventFilterList = (Set) config.get(keyREventFilterList); Assert.assertEquals(Objects.requireNonNull(eventFilterList).toString(), countly.moduleConfiguration.getEventFilterSet().toString()); - JSONArray userPropertyFilterList = (JSONArray) config.get(keyRUserPropertyFilterList); + Set userPropertyFilterList = (Set) config.get(keyRUserPropertyFilterList); Assert.assertEquals(Objects.requireNonNull(userPropertyFilterList).toString(), countly.moduleConfiguration.getUserPropertyFilterSet().toString()); - JSONArray segmentationFilterList = (JSONArray) config.get(keyRSegmentationFilterList); + Set segmentationFilterList = (Set) config.get(keyRSegmentationFilterList); Assert.assertEquals(Objects.requireNonNull(segmentationFilterList).toString(), countly.moduleConfiguration.getSegmentationFilterSet().toString()); - JSONObject eventSegmentationFilterMap = (JSONObject) config.get(keyREventSegmentationFilterList); + Map> eventSegmentationFilterMap = (Map>) config.get(keyREventSegmentationFilterList); Assert.assertEquals(Objects.requireNonNull(eventSegmentationFilterMap).toString(), countly.moduleConfiguration.getEventSegmentationFilterMap().toString()); } } \ No newline at end of file From c8af3cbd21a27543bff2c39ff7c3bb5a3c9fca10 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Fri, 19 Dec 2025 13:58:57 +0300 Subject: [PATCH 07/14] refactor: update list filters according to individual filtering --- .../android/sdk/ConfigurationProvider.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/sdk/src/main/java/ly/count/android/sdk/ConfigurationProvider.java b/sdk/src/main/java/ly/count/android/sdk/ConfigurationProvider.java index ed8b2de05..74e78a8dd 100644 --- a/sdk/src/main/java/ly/count/android/sdk/ConfigurationProvider.java +++ b/sdk/src/main/java/ly/count/android/sdk/ConfigurationProvider.java @@ -38,13 +38,22 @@ interface ConfigurationProvider { int getUserPropertyCacheLimit(); // LISTING FILTERS - boolean getFilterIsWhitelist(); - Set getEventFilterSet(); + FilterList> getEventFilterList(); - Set getUserPropertyFilterSet(); + FilterList> getUserPropertyFilterList(); - Set getSegmentationFilterSet(); + FilterList> getSegmentationFilterList(); - Map> getEventSegmentationFilterMap(); + FilterList>> getEventSegmentationFilterList(); + + class FilterList { + T filterList; + boolean isWhitelist; + + FilterList(T filterList, boolean isWhitelist) { + this.filterList = filterList; + this.isWhitelist = isWhitelist; + } + } } From 453b9f70040fc8392054f2f84338ee94a0887e50 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Fri, 19 Dec 2025 13:59:23 +0300 Subject: [PATCH 08/14] refactor: individual listing impl --- .../android/sdk/ModuleConfiguration.java | 116 ++++++++++++------ 1 file changed, 77 insertions(+), 39 deletions(-) diff --git a/sdk/src/main/java/ly/count/android/sdk/ModuleConfiguration.java b/sdk/src/main/java/ly/count/android/sdk/ModuleConfiguration.java index 4c50e0488..95f094ec9 100644 --- a/sdk/src/main/java/ly/count/android/sdk/ModuleConfiguration.java +++ b/sdk/src/main/java/ly/count/android/sdk/ModuleConfiguration.java @@ -53,11 +53,15 @@ class ModuleConfiguration extends ModuleBase implements ConfigurationProvider { final static String keyRBOMRequestAge = "bom_ra"; final static String keyRBOMDuration = "bom_d"; final static String keyRUserPropertyCacheLimit = "upcl"; - final static String keyRFilterPreset = "filter_preset"; - final static String keyREventFilterList = "eb"; - final static String keyRUserPropertyFilterList = "upb"; - final static String keyRSegmentationFilterList = "sb"; - final static String keyREventSegmentationFilterList = "esb"; // json + final static String keyREventBlacklist = "eb"; + final static String keyRUserPropertyBlacklist = "upb"; + final static String keyRSegmentationBlacklist = "sb"; + final static String keyREventSegmentationBlacklist = "esb"; // json + final static String keyREventWhitelist = "ew"; + final static String keyRUserPropertyWhitelist = "upw"; + final static String keyRSegmentationWhitelist = "sw"; + final static String keyREventSegmentationWhitelist = "esw"; // json + // FLAGS boolean currentVTracking = true; boolean currentVNetworking = true; @@ -78,11 +82,10 @@ class ModuleConfiguration extends ModuleBase implements ConfigurationProvider { int currentVUserPropertyCacheLimit = 100; // FILTERS - String currentVFilterPreset = "Blacklisting"; - Set currentVEventFilterList = new HashSet<>(); - Set currentVUserPropertyFilterList = new HashSet<>(); - Set currentVSegmentationFilterList = new HashSet<>(); - Map> currentVEventSegmentationFilterList = new ConcurrentHashMap<>(); + FilterList> currentVEventFilterList = new FilterList<>(new HashSet<>(), false); + FilterList> currentVUserPropertyFilterList = new FilterList<>(new HashSet<>(), false); + FilterList> currentVSegmentationFilterList = new FilterList<>(new HashSet<>(), false); + FilterList>> currentVEventSegmentationFilterList = new FilterList<>(new ConcurrentHashMap<>(), false); // SERVER CONFIGURATION PARAMS Integer serverConfigUpdateInterval; // in hours @@ -227,7 +230,6 @@ private void updateConfigVariables(@NonNull final CountlyConfig clyConfig) { currentVBOMRequestAge = extractValue(keyRBOMRequestAge, sb, currentVBOMRequestAge, currentVBOMRequestAge, Integer.class, (Integer value) -> value > 0); currentVBOMDuration = extractValue(keyRBOMDuration, sb, currentVBOMDuration, currentVBOMDuration, Integer.class, (Integer value) -> value > 0); currentVUserPropertyCacheLimit = extractValue(keyRUserPropertyCacheLimit, sb, currentVUserPropertyCacheLimit, currentVUserPropertyCacheLimit, Integer.class, (Integer value) -> value > 0); - currentVFilterPreset = extractValue(keyRFilterPreset, sb, currentVFilterPreset, currentVFilterPreset, String.class, (String value) -> value.equals("Blacklisting") || value.equals("Whitelisting")); clyConfig.setMaxRequestQueueSize(extractValue(keyRReqQueueSize, sb, clyConfig.maxRequestQueueSize, clyConfig.maxRequestQueueSize, Integer.class, (Integer value) -> value > 0)); clyConfig.setEventQueueSizeToSend(extractValue(keyREventQueueSize, sb, clyConfig.eventQueueSizeThreshold, Countly.sharedInstance().EVENT_QUEUE_SIZE_THRESHOLD, Integer.class, (Integer value) -> value > 0)); @@ -253,24 +255,63 @@ private void updateConfigVariables(@NonNull final CountlyConfig clyConfig) { } private void updateListingFilters() { - JSONArray eventFilterListJSARR = latestRetrievedConfiguration.optJSONArray(keyREventFilterList); - JSONArray userPropertyFilterListJSARR = latestRetrievedConfiguration.optJSONArray(keyRUserPropertyFilterList); - JSONArray segmentationFilterListJSARR = latestRetrievedConfiguration.optJSONArray(keyRSegmentationFilterList); - JSONObject eventSegmentationFilterListJSOBJ = latestRetrievedConfiguration.optJSONObject(keyREventSegmentationFilterList); - - extractFilterSetFromJSONArray(eventFilterListJSARR, currentVEventFilterList); - extractFilterSetFromJSONArray(userPropertyFilterListJSARR, currentVUserPropertyFilterList); - extractFilterSetFromJSONArray(segmentationFilterListJSARR, currentVSegmentationFilterList); - if (eventSegmentationFilterListJSOBJ != null) { - currentVEventSegmentationFilterList.clear(); - Iterator keys = eventSegmentationFilterListJSOBJ.keys(); + JSONArray eventBlacklistJSARR = latestRetrievedConfiguration.optJSONArray(keyREventBlacklist); + JSONArray eventWhitelistJSARR = latestRetrievedConfiguration.optJSONArray(keyREventWhitelist); + JSONArray userPropertyBlacklistJSARR = latestRetrievedConfiguration.optJSONArray(keyRUserPropertyBlacklist); + JSONArray userPropertyWhitelistJSARR = latestRetrievedConfiguration.optJSONArray(keyRUserPropertyWhitelist); + JSONArray segmentationBlacklistJSARR = latestRetrievedConfiguration.optJSONArray(keyRSegmentationBlacklist); + JSONArray segmentationWhitelistJSARR = latestRetrievedConfiguration.optJSONArray(keyRSegmentationWhitelist); + JSONObject eventSegmentationBlacklistJSOBJ = latestRetrievedConfiguration.optJSONObject(keyREventSegmentationBlacklist); + JSONObject eventSegmentationWhitelistJSOBJ = latestRetrievedConfiguration.optJSONObject(keyREventSegmentationWhitelist); + + if (eventWhitelistJSARR != null) { + extractFilterSetFromJSONArray(eventWhitelistJSARR, currentVEventFilterList.filterList); + currentVEventFilterList.isWhitelist = true; + } else if (eventBlacklistJSARR != null) { + extractFilterSetFromJSONArray(eventBlacklistJSARR, currentVEventFilterList.filterList); + currentVEventFilterList.isWhitelist = false; + } + + if (userPropertyWhitelistJSARR != null) { + extractFilterSetFromJSONArray(userPropertyWhitelistJSARR, currentVUserPropertyFilterList.filterList); + currentVUserPropertyFilterList.isWhitelist = true; + } else if (userPropertyBlacklistJSARR != null) { + extractFilterSetFromJSONArray(userPropertyBlacklistJSARR, currentVUserPropertyFilterList.filterList); + currentVUserPropertyFilterList.isWhitelist = false; + } + + if (segmentationWhitelistJSARR != null) { + extractFilterSetFromJSONArray(segmentationWhitelistJSARR, currentVSegmentationFilterList.filterList); + currentVSegmentationFilterList.isWhitelist = true; + } else if (segmentationBlacklistJSARR != null) { + extractFilterSetFromJSONArray(segmentationBlacklistJSARR, currentVSegmentationFilterList.filterList); + currentVSegmentationFilterList.isWhitelist = false; + } + + if (eventSegmentationWhitelistJSOBJ != null) { + currentVEventSegmentationFilterList.filterList.clear(); + currentVEventSegmentationFilterList.isWhitelist = true; + Iterator keys = eventSegmentationWhitelistJSOBJ.keys(); while (keys.hasNext()) { String key = keys.next(); - JSONArray jsonArray = eventSegmentationFilterListJSOBJ.optJSONArray(key); + JSONArray jsonArray = eventSegmentationWhitelistJSOBJ.optJSONArray(key); if (jsonArray != null) { Set filterSet = new HashSet<>(); extractFilterSetFromJSONArray(jsonArray, filterSet); - currentVEventSegmentationFilterList.put(key, filterSet); + currentVEventSegmentationFilterList.filterList.put(key, filterSet); + } + } + } else if (eventSegmentationBlacklistJSOBJ != null) { + currentVEventSegmentationFilterList.filterList.clear(); + currentVEventSegmentationFilterList.isWhitelist = false; + Iterator keys = eventSegmentationBlacklistJSOBJ.keys(); + while (keys.hasNext()) { + String key = keys.next(); + JSONArray jsonArray = eventSegmentationBlacklistJSOBJ.optJSONArray(key); + if (jsonArray != null) { + Set filterSet = new HashSet<>(); + extractFilterSetFromJSONArray(jsonArray, filterSet); + currentVEventSegmentationFilterList.filterList.put(key, filterSet); } } } @@ -373,15 +414,16 @@ private void removeUnsupportedKeys(@NonNull JSONObject newInner) { break; // --- Filtering keys --- - case keyRFilterPreset: - isValid = value instanceof String && (value.equals("Blacklisting") || value.equals("Whitelisting")); - break; - case keyREventFilterList: - case keyRUserPropertyFilterList: - case keyRSegmentationFilterList: + case keyREventBlacklist: + case keyRSegmentationBlacklist: + case keyRUserPropertyBlacklist: + case keyREventWhitelist: + case keyRSegmentationWhitelist: + case keyRUserPropertyWhitelist: isValid = value instanceof JSONArray; break; - case keyREventSegmentationFilterList: + case keyREventSegmentationBlacklist: + case keyREventSegmentationWhitelist: isValid = value instanceof JSONObject; break; // --- Unknown keys --- @@ -577,23 +619,19 @@ public boolean getTrackingEnabled() { return currentVUserPropertyCacheLimit; } - @Override public boolean getFilterIsWhitelist() { - return currentVFilterPreset.equals("Whitelisting"); - } - - @Override public Set getEventFilterSet() { + @Override public FilterList> getEventFilterList() { return currentVEventFilterList; } - @Override public Set getUserPropertyFilterSet() { + @Override public FilterList> getUserPropertyFilterList() { return currentVUserPropertyFilterList; } - @Override public Set getSegmentationFilterSet() { + @Override public FilterList> getSegmentationFilterList() { return currentVSegmentationFilterList; } - @Override public Map> getEventSegmentationFilterMap() { + @Override public FilterList>> getEventSegmentationFilterList() { return currentVEventSegmentationFilterList; } } From 0ac74e1eaf8893e4845789b785295d14f6c03a08 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Fri, 19 Dec 2025 13:59:38 +0300 Subject: [PATCH 09/14] refactor: test helper update --- .../android/sdk/ServerConfigBuilder.java | 89 ++++++++++++------- 1 file changed, 56 insertions(+), 33 deletions(-) diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java b/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java index a5ea17e9f..eebb94f3b 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java @@ -17,10 +17,11 @@ import static ly.count.android.sdk.ModuleConfiguration.keyRCustomEventTracking; import static ly.count.android.sdk.ModuleConfiguration.keyRDropOldRequestTime; import static ly.count.android.sdk.ModuleConfiguration.keyREnterContentZone; -import static ly.count.android.sdk.ModuleConfiguration.keyREventFilterList; +import static ly.count.android.sdk.ModuleConfiguration.keyREventBlacklist; import static ly.count.android.sdk.ModuleConfiguration.keyREventQueueSize; -import static ly.count.android.sdk.ModuleConfiguration.keyREventSegmentationFilterList; -import static ly.count.android.sdk.ModuleConfiguration.keyRFilterPreset; +import static ly.count.android.sdk.ModuleConfiguration.keyREventSegmentationBlacklist; +import static ly.count.android.sdk.ModuleConfiguration.keyREventSegmentationWhitelist; +import static ly.count.android.sdk.ModuleConfiguration.keyREventWhitelist; import static ly.count.android.sdk.ModuleConfiguration.keyRLimitBreadcrumb; import static ly.count.android.sdk.ModuleConfiguration.keyRLimitKeyLength; import static ly.count.android.sdk.ModuleConfiguration.keyRLimitSegValues; @@ -32,14 +33,16 @@ import static ly.count.android.sdk.ModuleConfiguration.keyRNetworking; import static ly.count.android.sdk.ModuleConfiguration.keyRRefreshContentZone; import static ly.count.android.sdk.ModuleConfiguration.keyRReqQueueSize; -import static ly.count.android.sdk.ModuleConfiguration.keyRSegmentationFilterList; +import static ly.count.android.sdk.ModuleConfiguration.keyRSegmentationBlacklist; +import static ly.count.android.sdk.ModuleConfiguration.keyRSegmentationWhitelist; import static ly.count.android.sdk.ModuleConfiguration.keyRServerConfigUpdateInterval; import static ly.count.android.sdk.ModuleConfiguration.keyRSessionTracking; import static ly.count.android.sdk.ModuleConfiguration.keyRSessionUpdateInterval; import static ly.count.android.sdk.ModuleConfiguration.keyRTimestamp; import static ly.count.android.sdk.ModuleConfiguration.keyRTracking; +import static ly.count.android.sdk.ModuleConfiguration.keyRUserPropertyBlacklist; import static ly.count.android.sdk.ModuleConfiguration.keyRUserPropertyCacheLimit; -import static ly.count.android.sdk.ModuleConfiguration.keyRUserPropertyFilterList; +import static ly.count.android.sdk.ModuleConfiguration.keyRUserPropertyWhitelist; import static ly.count.android.sdk.ModuleConfiguration.keyRVersion; import static ly.count.android.sdk.ModuleConfiguration.keyRViewTracking; @@ -184,28 +187,39 @@ ServerConfigBuilder userPropertyCacheLimit(int limit) { return this; } - ServerConfigBuilder filterPreset(String preset) { - config.put(keyRFilterPreset, preset); - return this; - } - - ServerConfigBuilder eventFilterList(Set filterList) { - config.put(keyREventFilterList, filterList); + ServerConfigBuilder eventFilterList(Set filterList, boolean isWhitelist) { + if (isWhitelist) { + config.put(keyREventWhitelist, filterList); + } else { + config.put(keyREventBlacklist, filterList); + } return this; } - ServerConfigBuilder userPropertyFilterList(Set filterList) { - config.put(keyRUserPropertyFilterList, filterList); + ServerConfigBuilder userPropertyFilterList(Set filterList, boolean isWhitelist) { + if (isWhitelist) { + config.put(keyRUserPropertyWhitelist, filterList); + } else { + config.put(keyRUserPropertyBlacklist, filterList); + } return this; } - ServerConfigBuilder segmentationFilterList(Set filterList) { - config.put(keyRSegmentationFilterList, filterList); + ServerConfigBuilder segmentationFilterList(Set filterList, boolean isWhitelist) { + if (isWhitelist) { + config.put(keyRSegmentationWhitelist, filterList); + } else { + config.put(keyRSegmentationBlacklist, filterList); + } return this; } - ServerConfigBuilder eventSegmentationFilterMap(Map> filterMap) { - config.put(keyREventSegmentationFilterList, filterMap); + ServerConfigBuilder eventSegmentationFilterMap(Map> filterMap, boolean isWhitelist) { + if (isWhitelist) { + config.put(keyREventSegmentationWhitelist, filterMap); + } else { + config.put(keyREventSegmentationBlacklist, filterMap); + } return this; } @@ -240,11 +254,10 @@ ServerConfigBuilder defaults() { traceLinesLimit(Countly.maxStackTraceLinesPerThreadDefault); userPropertyCacheLimit(100); - filterPreset("Blacklisting"); - eventFilterList(new HashSet<>()); - userPropertyFilterList(new HashSet<>()); - segmentationFilterList(new HashSet<>()); - eventSegmentationFilterMap(new ConcurrentHashMap<>()); + eventFilterList(new HashSet<>(), false); + userPropertyFilterList(new HashSet<>(), false); + segmentationFilterList(new HashSet<>(), false); + eventSegmentationFilterMap(new ConcurrentHashMap<>(), false); return this; } @@ -312,18 +325,28 @@ private void validateLimits(Countly countly) { } private void validateFilterSettings(Countly countly) { - Assert.assertEquals(config.get(keyRFilterPreset), countly.moduleConfiguration.currentVFilterPreset); - - Set eventFilterList = (Set) config.get(keyREventFilterList); - Assert.assertEquals(Objects.requireNonNull(eventFilterList).toString(), countly.moduleConfiguration.getEventFilterSet().toString()); + Set eventFilterList = (Set) config.get(keyREventSegmentationBlacklist); + if (eventFilterList == null) { + eventFilterList = (Set) config.get(keyREventSegmentationWhitelist); + } + Assert.assertEquals(Objects.requireNonNull(eventFilterList).toString(), countly.moduleConfiguration.getEventFilterList().filterList.toString()); - Set userPropertyFilterList = (Set) config.get(keyRUserPropertyFilterList); - Assert.assertEquals(Objects.requireNonNull(userPropertyFilterList).toString(), countly.moduleConfiguration.getUserPropertyFilterSet().toString()); + Set userPropertyFilterList = (Set) config.get(keyRUserPropertyBlacklist); + if (userPropertyFilterList == null) { + userPropertyFilterList = (Set) config.get(keyRUserPropertyWhitelist); + } + Assert.assertEquals(Objects.requireNonNull(userPropertyFilterList).toString(), countly.moduleConfiguration.getUserPropertyFilterList().filterList.toString()); - Set segmentationFilterList = (Set) config.get(keyRSegmentationFilterList); - Assert.assertEquals(Objects.requireNonNull(segmentationFilterList).toString(), countly.moduleConfiguration.getSegmentationFilterSet().toString()); + Set segmentationFilterList = (Set) config.get(keyRSegmentationBlacklist); + if (segmentationFilterList == null) { + segmentationFilterList = (Set) config.get(keyRSegmentationWhitelist); + } + Assert.assertEquals(Objects.requireNonNull(segmentationFilterList).toString(), countly.moduleConfiguration.getSegmentationFilterList().filterList.toString()); - Map> eventSegmentationFilterMap = (Map>) config.get(keyREventSegmentationFilterList); - Assert.assertEquals(Objects.requireNonNull(eventSegmentationFilterMap).toString(), countly.moduleConfiguration.getEventSegmentationFilterMap().toString()); + Map> eventSegmentationFilterMap = (Map>) config.get(keyREventSegmentationBlacklist); + if (eventSegmentationFilterMap == null) { + eventSegmentationFilterMap = (Map>) config.get(keyREventSegmentationWhitelist); + } + Assert.assertEquals(Objects.requireNonNull(eventSegmentationFilterMap).toString(), countly.moduleConfiguration.getEventSegmentationFilterList().filterList.toString()); } } \ No newline at end of file From e1e207995b2d8d003c1f3596e591b7c74151741b Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Fri, 19 Dec 2025 13:59:53 +0300 Subject: [PATCH 10/14] refactor: helper individual udpate --- .../count/android/sdk/UtilsListingFilters.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sdk/src/main/java/ly/count/android/sdk/UtilsListingFilters.java b/sdk/src/main/java/ly/count/android/sdk/UtilsListingFilters.java index 985a7acf2..53bebb174 100644 --- a/sdk/src/main/java/ly/count/android/sdk/UtilsListingFilters.java +++ b/sdk/src/main/java/ly/count/android/sdk/UtilsListingFilters.java @@ -11,32 +11,36 @@ private UtilsListingFilters() { } static boolean applyEventFilter(@NonNull String eventName, @NonNull ConfigurationProvider configProvider) { - return applyListFilter(eventName, configProvider.getEventFilterSet(), configProvider.getFilterIsWhitelist()); + ConfigurationProvider.FilterList> eventFilterList = configProvider.getEventFilterList(); + return applyListFilter(eventName, eventFilterList.filterList, eventFilterList.isWhitelist); } static boolean applyUserPropertyFilter(@NonNull String propertyName, @NonNull ConfigurationProvider configProvider) { - return applyListFilter(propertyName, configProvider.getUserPropertyFilterSet(), configProvider.getFilterIsWhitelist()); + ConfigurationProvider.FilterList> userPropertyFilterList = configProvider.getUserPropertyFilterList(); + return applyListFilter(propertyName, userPropertyFilterList.filterList, userPropertyFilterList.isWhitelist); } static void applySegmentationFilter(@NonNull Map segmentation, @NonNull ConfigurationProvider configProvider, @NonNull ModuleLog L) { if (segmentation.isEmpty()) { return; } - applyMapFilter(segmentation, configProvider.getSegmentationFilterSet(), configProvider.getFilterIsWhitelist(), L); + + applyMapFilter(segmentation, configProvider.getSegmentationFilterList().filterList, configProvider.getSegmentationFilterList().isWhitelist, L); } static void applyEventSegmentationFilter(@NonNull String eventName, @NonNull Map segmentation, @NonNull ConfigurationProvider configProvider, @NonNull ModuleLog L) { - if (segmentation.isEmpty() || configProvider.getEventSegmentationFilterMap().isEmpty()) { + ConfigurationProvider.FilterList>> eventSegmentationFilterList = configProvider.getEventSegmentationFilterList(); + if (segmentation.isEmpty() || eventSegmentationFilterList.filterList.isEmpty()) { return; } - Set segmentationSet = configProvider.getEventSegmentationFilterMap().get(eventName); + Set segmentationSet = eventSegmentationFilterList.filterList.get(eventName); if (segmentationSet == null || segmentationSet.isEmpty()) { // No rules defined for this event so allow everything return; } - applyMapFilter(segmentation, segmentationSet, configProvider.getFilterIsWhitelist(), L); + applyMapFilter(segmentation, segmentationSet, eventSegmentationFilterList.isWhitelist, L); } private static void applyMapFilter(@NonNull Map map, @NonNull Set filterSet, boolean isWhitelist, @NonNull ModuleLog L) { From 1d1850c51c2e6d0de2e105cf6660bc7e1fb4e14f Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Fri, 19 Dec 2025 14:00:18 +0300 Subject: [PATCH 11/14] feat: tests update --- .../android/sdk/ConnectionProcessorTests.java | 20 ++++++++----------- .../android/sdk/ModuleConfigurationTests.java | 9 ++++----- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/ConnectionProcessorTests.java b/sdk/src/androidTest/java/ly/count/android/sdk/ConnectionProcessorTests.java index 17a3be946..35aec523b 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/ConnectionProcessorTests.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/ConnectionProcessorTests.java @@ -138,24 +138,20 @@ public void setUp() { return 100; } - @Override public boolean getFilterIsWhitelist() { - return false; - } - - @Override public Set getEventFilterSet() { - return new HashSet<>(); + @Override public FilterList> getEventFilterList() { + return new FilterList<>(new HashSet<>(), false); } - @Override public Set getUserPropertyFilterSet() { - return new HashSet<>(); + @Override public FilterList> getUserPropertyFilterList() { + return new FilterList<>(new HashSet<>(), false); } - @Override public Set getSegmentationFilterSet() { - return new HashSet<>(); + @Override public FilterList> getSegmentationFilterList() { + return new FilterList<>(new HashSet<>(), false); } - @Override public Map> getEventSegmentationFilterMap() { - return new ConcurrentHashMap<>(); + @Override public FilterList>> getEventSegmentationFilterList() { + return new FilterList<>(new ConcurrentHashMap<>(), false); } }; diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java b/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java index e3b3917ce..79715b33d 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java @@ -1128,11 +1128,10 @@ private void initServerConfigWithValues(BiConsumer config .userPropertyCacheLimit(67) // Filters - .filterPreset("Whitelisting") - .eventFilterList(new HashSet<>()) - .userPropertyFilterList(new HashSet<>()) - .segmentationFilterList(new HashSet<>()) - .eventSegmentationFilterMap(new ConcurrentHashMap<>()); + .eventFilterList(new HashSet<>(), false) + .userPropertyFilterList(new HashSet<>(), false) + .segmentationFilterList(new HashSet<>(), false) + .eventSegmentationFilterMap(new ConcurrentHashMap<>(), false); String serverConfig = builder.build(); CountlyConfig countlyConfig = TestUtils.createBaseConfig().setLoggingEnabled(false); From 0d5bb3b225c46dd350acc487add15a0e5e36d5f1 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Fri, 19 Dec 2025 14:01:11 +0300 Subject: [PATCH 12/14] feat: tests fix misclass --- .../java/ly/count/android/sdk/ServerConfigBuilder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java b/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java index eebb94f3b..b6c8bdd20 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/ServerConfigBuilder.java @@ -325,9 +325,9 @@ private void validateLimits(Countly countly) { } private void validateFilterSettings(Countly countly) { - Set eventFilterList = (Set) config.get(keyREventSegmentationBlacklist); + Set eventFilterList = (Set) config.get(keyREventBlacklist); if (eventFilterList == null) { - eventFilterList = (Set) config.get(keyREventSegmentationWhitelist); + eventFilterList = (Set) config.get(keyREventWhitelist); } Assert.assertEquals(Objects.requireNonNull(eventFilterList).toString(), countly.moduleConfiguration.getEventFilterList().filterList.toString()); From 93ea2d5a7ef10055f9f1a7cc14f47e62ce5f2d97 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Fri, 19 Dec 2025 14:01:49 +0300 Subject: [PATCH 13/14] fix: update count --- .../java/ly/count/android/sdk/ModuleConfigurationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java b/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java index 79715b33d..5a44cbe9a 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java @@ -645,7 +645,7 @@ public void invalidConfigResponses_AreRejected() { */ @Test public void configurationParameterCount() { - int configParameterCount = 37; // plus config, timestamp and version parameters, UPDATE: list filters, and user property cache limit + int configParameterCount = 40; // plus config, timestamp and version parameters, UPDATE: list filters, and user property cache limit int count = 0; for (Field field : ModuleConfiguration.class.getDeclaredFields()) { if (field.getName().startsWith("keyR")) { From f43ab84febf0ae525bc1cbf6c5d963ad67e2aab5 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Mon, 22 Dec 2025 15:23:17 +0300 Subject: [PATCH 14/14] feat: some logging --- .../android/sdk/ModuleConfiguration.java | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/sdk/src/main/java/ly/count/android/sdk/ModuleConfiguration.java b/sdk/src/main/java/ly/count/android/sdk/ModuleConfiguration.java index 95f094ec9..2e2046e71 100644 --- a/sdk/src/main/java/ly/count/android/sdk/ModuleConfiguration.java +++ b/sdk/src/main/java/ly/count/android/sdk/ModuleConfiguration.java @@ -255,6 +255,11 @@ private void updateConfigVariables(@NonNull final CountlyConfig clyConfig) { } private void updateListingFilters() { + L.d("[ModuleConfiguration] updateListingFilters, current listing filters before updating: \n" + + "Event Filter List: " + currentVEventFilterList.filterList + ", isWhitelist: " + currentVEventFilterList.isWhitelist + "\n" + + "User Property Filter List: " + currentVUserPropertyFilterList.filterList + ", isWhitelist: " + currentVUserPropertyFilterList.isWhitelist + "\n" + + "Segmentation Filter List: " + currentVSegmentationFilterList.filterList + ", isWhitelist: " + currentVSegmentationFilterList.isWhitelist + "\n" + + "Event Segmentation Filter List: " + currentVEventSegmentationFilterList.filterList + ", isWhitelist: " + currentVEventSegmentationFilterList.isWhitelist); JSONArray eventBlacklistJSARR = latestRetrievedConfiguration.optJSONArray(keyREventBlacklist); JSONArray eventWhitelistJSARR = latestRetrievedConfiguration.optJSONArray(keyREventWhitelist); JSONArray userPropertyBlacklistJSARR = latestRetrievedConfiguration.optJSONArray(keyRUserPropertyBlacklist); @@ -264,50 +269,50 @@ private void updateListingFilters() { JSONObject eventSegmentationBlacklistJSOBJ = latestRetrievedConfiguration.optJSONObject(keyREventSegmentationBlacklist); JSONObject eventSegmentationWhitelistJSOBJ = latestRetrievedConfiguration.optJSONObject(keyREventSegmentationWhitelist); - if (eventWhitelistJSARR != null) { - extractFilterSetFromJSONArray(eventWhitelistJSARR, currentVEventFilterList.filterList); - currentVEventFilterList.isWhitelist = true; - } else if (eventBlacklistJSARR != null) { + if (eventBlacklistJSARR != null) { extractFilterSetFromJSONArray(eventBlacklistJSARR, currentVEventFilterList.filterList); currentVEventFilterList.isWhitelist = false; + } else if (eventWhitelistJSARR != null) { + extractFilterSetFromJSONArray(eventWhitelistJSARR, currentVEventFilterList.filterList); + currentVEventFilterList.isWhitelist = true; } - if (userPropertyWhitelistJSARR != null) { - extractFilterSetFromJSONArray(userPropertyWhitelistJSARR, currentVUserPropertyFilterList.filterList); - currentVUserPropertyFilterList.isWhitelist = true; - } else if (userPropertyBlacklistJSARR != null) { + if (userPropertyBlacklistJSARR != null) { extractFilterSetFromJSONArray(userPropertyBlacklistJSARR, currentVUserPropertyFilterList.filterList); currentVUserPropertyFilterList.isWhitelist = false; + } else if (userPropertyWhitelistJSARR != null) { + extractFilterSetFromJSONArray(userPropertyWhitelistJSARR, currentVUserPropertyFilterList.filterList); + currentVUserPropertyFilterList.isWhitelist = true; } - if (segmentationWhitelistJSARR != null) { - extractFilterSetFromJSONArray(segmentationWhitelistJSARR, currentVSegmentationFilterList.filterList); - currentVSegmentationFilterList.isWhitelist = true; - } else if (segmentationBlacklistJSARR != null) { + if (segmentationBlacklistJSARR != null) { extractFilterSetFromJSONArray(segmentationBlacklistJSARR, currentVSegmentationFilterList.filterList); currentVSegmentationFilterList.isWhitelist = false; + } else if (segmentationWhitelistJSARR != null) { + extractFilterSetFromJSONArray(segmentationWhitelistJSARR, currentVSegmentationFilterList.filterList); + currentVSegmentationFilterList.isWhitelist = true; } - if (eventSegmentationWhitelistJSOBJ != null) { + if (eventSegmentationBlacklistJSOBJ != null) { currentVEventSegmentationFilterList.filterList.clear(); - currentVEventSegmentationFilterList.isWhitelist = true; - Iterator keys = eventSegmentationWhitelistJSOBJ.keys(); + currentVEventSegmentationFilterList.isWhitelist = false; + Iterator keys = eventSegmentationBlacklistJSOBJ.keys(); while (keys.hasNext()) { String key = keys.next(); - JSONArray jsonArray = eventSegmentationWhitelistJSOBJ.optJSONArray(key); + JSONArray jsonArray = eventSegmentationBlacklistJSOBJ.optJSONArray(key); if (jsonArray != null) { Set filterSet = new HashSet<>(); extractFilterSetFromJSONArray(jsonArray, filterSet); currentVEventSegmentationFilterList.filterList.put(key, filterSet); } } - } else if (eventSegmentationBlacklistJSOBJ != null) { + } else if (eventSegmentationWhitelistJSOBJ != null) { currentVEventSegmentationFilterList.filterList.clear(); - currentVEventSegmentationFilterList.isWhitelist = false; - Iterator keys = eventSegmentationBlacklistJSOBJ.keys(); + currentVEventSegmentationFilterList.isWhitelist = true; + Iterator keys = eventSegmentationWhitelistJSOBJ.keys(); while (keys.hasNext()) { String key = keys.next(); - JSONArray jsonArray = eventSegmentationBlacklistJSOBJ.optJSONArray(key); + JSONArray jsonArray = eventSegmentationWhitelistJSOBJ.optJSONArray(key); if (jsonArray != null) { Set filterSet = new HashSet<>(); extractFilterSetFromJSONArray(jsonArray, filterSet); @@ -315,6 +320,12 @@ private void updateListingFilters() { } } } + + L.d("[ModuleConfiguration] updateListingFilters, current listing filters after updating: \n" + + "Event Filter List: " + currentVEventFilterList.filterList + ", isWhitelist: " + currentVEventFilterList.isWhitelist + "\n" + + "User Property Filter List: " + currentVUserPropertyFilterList.filterList + ", isWhitelist: " + currentVUserPropertyFilterList.isWhitelist + "\n" + + "Segmentation Filter List: " + currentVSegmentationFilterList.filterList + ", isWhitelist: " + currentVSegmentationFilterList.isWhitelist + "\n" + + "Event Segmentation Filter List: " + currentVEventSegmentationFilterList.filterList + ", isWhitelist: " + currentVEventSegmentationFilterList.isWhitelist); } private void extractFilterSetFromJSONArray(@Nullable JSONArray jsonArray, @NonNull Set targetSet) {