-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle being able to be minified by ProGuard #43
Changes from 3 commits
d98a4dd
02b721b
54227ed
774d935
0a4c254
ddf65d7
2b7fa61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package cloud.eppo.android; | ||
|
||
public interface CacheLoadCallback { | ||
void onCacheLoadSuccess(); | ||
|
||
void onCacheLoadFail(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,13 +22,15 @@ | |
import cloud.eppo.android.dto.FlagConfig; | ||
import cloud.eppo.android.dto.RandomizationConfigResponse; | ||
import cloud.eppo.android.dto.adapters.EppoValueAdapter; | ||
import cloud.eppo.android.dto.deserializers.RandomizationConfigResponseDeserializer; | ||
import cloud.eppo.android.util.Utils; | ||
|
||
public class ConfigurationStore { | ||
|
||
private static final String TAG = logTag(ConfigurationStore.class); | ||
|
||
private final Gson gson = new GsonBuilder() | ||
.registerTypeAdapter(RandomizationConfigResponse.class, new RandomizationConfigResponseDeserializer()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We now use a custom (vs. automagic) deserializer. This way, upstream users of our SDK who also use ProGuard modification don't need to modify their It will also protect against accidentally breaking things if we add new classes that for whatever reason are not covered by the rules, gson is updated, etc. |
||
.registerTypeAdapter(EppoValue.class, new EppoValueAdapter()) | ||
.serializeNulls() | ||
.create(); | ||
|
@@ -43,10 +45,11 @@ public ConfigurationStore(Application application) { | |
this.sharedPrefs = Utils.getSharedPrefs(application); | ||
} | ||
|
||
public boolean loadFromCache(InitializationCallback callback) { | ||
public void loadFromCache(CacheLoadCallback callback) { | ||
if (flags != null || !cacheFile.exists()) { | ||
Log.d(TAG, "Not loading from cache ("+(flags == null ? "null flags" : "non-null flags")+")"); | ||
return false; | ||
callback.onCacheLoadFail(); | ||
return; | ||
} | ||
|
||
AsyncTask.execute(() -> { | ||
|
@@ -57,35 +60,28 @@ public boolean loadFromCache(InitializationCallback callback) { | |
RandomizationConfigResponse configResponse = gson.fromJson(reader, RandomizationConfigResponse.class); | ||
reader.close(); | ||
if (configResponse == null || configResponse.getFlags() == null) { | ||
// Invalid cached configuration, initialize as an empty map and delete file | ||
throw new JsonSyntaxException("Configuration file missing flags"); | ||
} | ||
flags = configResponse.getFlags(); | ||
updateConfigsInSharedPrefs(); | ||
} | ||
Log.d(TAG, "Cache loaded successfully"); | ||
callback.onCacheLoadSuccess(); | ||
} catch (Exception e) { | ||
Log.e(TAG, "Error loading from local cache", e); | ||
cacheFile.delete(); | ||
|
||
if (callback != null) { | ||
callback.onError("Unable to load config from cache"); | ||
} | ||
} | ||
|
||
if (callback != null) { | ||
callback.onCompleted(); | ||
callback.onCacheLoadFail(); | ||
} | ||
}); | ||
return true; | ||
} | ||
|
||
public void setFlags(Reader response) { | ||
RandomizationConfigResponse config = gson.fromJson(response, RandomizationConfigResponse.class); | ||
flags = config.getFlags(); | ||
if (flags == null) { | ||
if (config == null || config.getFlags() == null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Re-arrange to handle the null config edge case (shouldn't happen, but being defensive) |
||
Log.w(TAG, "Flags missing in configuration response"); | ||
flags = new ConcurrentHashMap<>(); | ||
} else { | ||
flags = config.getFlags(); | ||
} | ||
|
||
// update any existing flags already in shared prefs | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,25 @@ | ||
package cloud.eppo.android.dto; | ||
|
||
import java.util.List; | ||
import com.google.gson.annotations.SerializedName; | ||
|
||
public class Allocation { | ||
@SerializedName("percentExposure") | ||
private float percentExposure; | ||
|
||
@SerializedName("variations") | ||
private List<Variation> variations; | ||
|
||
public float getPercentExposure() { | ||
return percentExposure; | ||
} | ||
|
||
public void setPercentExposure(float percentExposure) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that we are not using automagic, we need explicitly-defined setters for our DTOs |
||
this.percentExposure = percentExposure; | ||
} | ||
|
||
public List<Variation> getVariations() { | ||
return variations; | ||
} | ||
|
||
public void setVariations(List<Variation> variations) { | ||
this.variations = variations; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,57 @@ | ||
package cloud.eppo.android.dto; | ||
|
||
import com.google.gson.annotations.SerializedName; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.HashMap; | ||
|
||
public class FlagConfig { | ||
@SerializedName("subjectShards") | ||
private int subjectShards; | ||
|
||
@SerializedName("enabled") | ||
private boolean enabled; | ||
|
||
@SerializedName("typedOverrides") | ||
private Map<String, String> typedOverrides = new HashMap<>(); | ||
|
||
@SerializedName("rules") | ||
private List<TargetingRule> rules; | ||
|
||
@SerializedName("allocations") | ||
private Map<String, Allocation> allocations; | ||
|
||
public int getSubjectShards() { | ||
return subjectShards; | ||
} | ||
|
||
public void setSubjectShards(int subjectShards) { | ||
this.subjectShards = subjectShards; | ||
} | ||
|
||
public boolean isEnabled() { | ||
return enabled; | ||
} | ||
|
||
public void setEnabled(boolean enabled) { | ||
this.enabled = enabled; | ||
} | ||
|
||
public Map<String, String> getTypedOverrides() { | ||
return typedOverrides; | ||
} | ||
|
||
public void setTypedOverrides(Map<String, String> typedOverrides) { | ||
this.typedOverrides = typedOverrides; | ||
} | ||
|
||
public Map<String, Allocation> getAllocations() { | ||
return allocations; | ||
} | ||
|
||
public void setAllocations(Map<String, Allocation> allocations) { | ||
this.allocations = allocations; | ||
} | ||
|
||
public List<TargetingRule> getRules() { | ||
return rules; | ||
} | ||
|
||
public void setRules(List<TargetingRule> rules) { | ||
this.rules = rules; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,15 @@ | ||
package cloud.eppo.android.dto; | ||
|
||
import java.util.concurrent.ConcurrentHashMap; | ||
import com.google.gson.annotations.SerializedName; | ||
|
||
public class RandomizationConfigResponse { | ||
@SerializedName("flags") | ||
private ConcurrentHashMap<String, FlagConfig> flags; | ||
|
||
public ConcurrentHashMap<String, FlagConfig> getFlags() { | ||
return flags; | ||
} | ||
|
||
public void setFlags(ConcurrentHashMap<String, FlagConfig> flags) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And this time, we don't need a |
||
this.flags = flags; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,14 +12,14 @@ public int getStart() { | |
return start; | ||
} | ||
|
||
public int getEnd() { | ||
return end; | ||
} | ||
|
||
public void setStart(int start) { | ||
this.start = start; | ||
} | ||
|
||
public int getEnd() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aiming for consistent |
||
return end; | ||
} | ||
|
||
public void setEnd(int end) { | ||
this.end = end; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,11 +13,17 @@ public String getAllocationKey() { | |
return allocationKey; | ||
} | ||
|
||
public void setAllocationKey(String allocationKey) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not cleanup There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤦 Yes, we absolutely should! Good catch! |
||
this.allocationKey = allocationKey; | ||
} | ||
|
||
public List<TargetingCondition> getConditions() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Related to Lisa's comment -- get and set conditions methods were already in place here, but also with the |
||
return conditions; | ||
} | ||
|
||
public void setConditions(List<TargetingCondition> conditions) { | ||
this.conditions = conditions; | ||
} | ||
|
||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of having the Cache operation call the passed-through
InitializationCallback,
it now has its own callback. This way, if loading the configuration from the cache fails but getting the configuration from the RAC succeeds, the success callback will called (instead of the failure callback).