3030import org .slf4j .Logger ;
3131
3232import java .util .Collections ;
33+ import java .util .HashMap ;
3334import java .util .Map ;
3435
3536/**
@@ -46,10 +47,51 @@ public class OptimizelyClient {
4647 private final Logger logger ;
4748
4849 @ Nullable private Optimizely optimizely ;
50+ @ Nullable private Map <String , String > defaultAttributes ;
4951
5052 OptimizelyClient (@ Nullable Optimizely optimizely , @ NonNull Logger logger ) {
5153 this .optimizely = optimizely ;
5254 this .logger = logger ;
55+ /*
56+ OptimizelyManager is initialized with an OptimizelyClient with a null optimizely property:
57+ https://github.com/optimizely/android-sdk/blob/master/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyManager.java#L63
58+ optimizely will remain null until OptimizelyManager#initialize has been called, so isValid checks for that. Otherwise apps would crash if
59+ the public methods here were called before initialize.
60+ So, we start with an empty map of default attributes until the manager is initialized.
61+ */
62+ defaultAttributes = Collections .EMPTY_MAP ;
63+ }
64+
65+ /**
66+ * Set default attributes to a non null attribute map.
67+ * This is set by the Optimizely manager and includes things like os version and sdk version.
68+ * @param attrs a map of default attributes.
69+ */
70+ protected void setDefaultAttributes (@ NonNull Map <String , String > attrs ) {
71+ this .defaultAttributes = attrs ;
72+ }
73+
74+ /**
75+ * Return the default attributes map
76+ * @return the map of default attributes
77+ */
78+ public @ NonNull Map <String , String > getDefaultAttributes () {
79+ return this .defaultAttributes ;
80+ }
81+
82+ /**
83+ * Get the default attributes and combine them with the attributes passed in.
84+ * The attributes passed in take precedence over the default attributes. So, you can override default attributes.
85+ * @param attrs attributes that will be combined with default attributes.
86+ * @return a new map of both the default attributes and attributes passed in.
87+ */
88+ private Map <String , String > getAllAttributes (@ NonNull Map <String , String > attrs ) {
89+ Map <String ,String > combinedMap = new HashMap <String ,String >(defaultAttributes );
90+
91+ // this essentially overrides defaultAttributes if the attrs passed in have the same key.
92+ combinedMap .putAll (attrs );
93+
94+ return combinedMap ;
5395 }
5496
5597 /**
@@ -83,7 +125,7 @@ public class OptimizelyClient {
83125 @ NonNull String userId ,
84126 @ NonNull Map <String , String > attributes ) {
85127 if (isValid ()) {
86- return optimizely .activate (experimentKey , userId , attributes );
128+ return optimizely .activate (experimentKey , userId , getAllAttributes ( attributes ) );
87129 } else {
88130 logger .warn ("Optimizely is not initialized, could not activate experiment {} for user {} " +
89131 "with attributes" , experimentKey , userId );
@@ -140,7 +182,7 @@ public void track(@NonNull String eventName,
140182 @ NonNull String userId ,
141183 @ NonNull Map <String , String > attributes ) throws UnknownEventTypeException {
142184 if (isValid ()) {
143- optimizely .track (eventName , userId , attributes );
185+ optimizely .track (eventName , userId , getAllAttributes ( attributes ) );
144186
145187 } else {
146188 logger .warn ("Optimizely is not initialized, could not track event {} for user {} with attributes" ,
@@ -160,7 +202,7 @@ public void track(@NonNull String eventName,
160202 @ NonNull Map <String , String > attributes ,
161203 @ NonNull Map <String , ?> eventTags ) throws UnknownEventTypeException {
162204 if (isValid ()) {
163- optimizely .track (eventName , userId , attributes , eventTags );
205+ optimizely .track (eventName , userId , getAllAttributes ( attributes ) , eventTags );
164206
165207 } else {
166208 logger .warn ("Optimizely is not initialized, could not track event {} for user {}" +
@@ -200,7 +242,7 @@ public void track(@NonNull String eventName,
200242 @ NonNull Map <String , String > attributes ,
201243 long eventValue ) {
202244 if (isValid ()) {
203- optimizely .track (eventName , userId , attributes , eventValue );
245+ optimizely .track (eventName , userId , getAllAttributes ( attributes ) , eventValue );
204246 } else {
205247 logger .warn ("Optimizely is not initialized, could not track event {} for user {}" +
206248 " with value {} and attributes" , eventName , userId , eventValue );
@@ -234,7 +276,7 @@ public void track(@NonNull String eventName,
234276 @ NonNull Map <String , String > attributes ,
235277 boolean activateExperiment ) {
236278 if (isValid ()) {
237- return optimizely .getVariableString (variableKey , userId , attributes ,
279+ return optimizely .getVariableString (variableKey , userId , getAllAttributes ( attributes ) ,
238280 activateExperiment );
239281 } else {
240282 logger .warn ("Optimizely is not initialized, could not get live variable {} " +
@@ -383,7 +425,7 @@ public void track(@NonNull String eventName,
383425 @ NonNull String userId ,
384426 @ NonNull Map <String , String > attributes ) {
385427 if (isValid ()) {
386- return optimizely .getVariation (experimentKey , userId , attributes );
428+ return optimizely .getVariation (experimentKey , userId , getAllAttributes ( attributes ) );
387429 } else {
388430 logger .warn ("Optimizely is not initialized, could not get variation for experiment {} " +
389431 "for user {} with attributes" , experimentKey , userId );
0 commit comments