diff --git a/analytics/src/main/java/com/segment/analytics/Analytics.java b/analytics/src/main/java/com/segment/analytics/Analytics.java index 2e77529b1..e423e15ec 100644 --- a/analytics/src/main/java/com/segment/analytics/Analytics.java +++ b/analytics/src/main/java/com/segment/analytics/Analytics.java @@ -781,7 +781,14 @@ private void waitForAdvertisingId() { void fillAndEnqueue(BasePayload.Builder builder, Options options) { waitForAdvertisingId(); - AnalyticsContext contextCopy = analyticsContext.unmodifiableCopy(); + AnalyticsContext contextCopy = new AnalyticsContext(analyticsContext); + + for (Map.Entry pair : options.context().entrySet()) { + contextCopy.put(pair.getKey(), pair.getValue()); + } + + contextCopy = contextCopy.unmodifiableCopy(); + builder.context(contextCopy); builder.anonymousId(contextCopy.traits().anonymousId()); builder.integrations(options.integrations()); diff --git a/analytics/src/main/java/com/segment/analytics/Options.java b/analytics/src/main/java/com/segment/analytics/Options.java index bad987183..ea3ee92d6 100644 --- a/analytics/src/main/java/com/segment/analytics/Options.java +++ b/analytics/src/main/java/com/segment/analytics/Options.java @@ -41,9 +41,11 @@ public class Options { public static final String ALL_INTEGRATIONS_KEY = "All"; private final Map integrations; // passed in by the user + private final Map context; public Options() { integrations = new ConcurrentHashMap<>(); + context = new ConcurrentHashMap<>(); } /** @@ -114,8 +116,26 @@ public Options setIntegrationOptions( return this; } + /** + * Attach some additional context information. Unlike with {@link + * com.segment.analytics.Analytics#getAnalyticsContext()}, this only has effect for this call. + * + * @param key The key of the extra context data + * @param value The value of the extra context data + * @return This options object for chaining + */ + public Options putContext(String key, Object value) { + context.put(key, value); + return this; + } + /** Returns a copy of settings for integrations. */ public Map integrations() { return new LinkedHashMap<>(integrations); } + + /** Returns a copy of the context. */ + public Map context() { + return new LinkedHashMap<>(context); + } } diff --git a/analytics/src/test/java/com/segment/analytics/AnalyticsTest.java b/analytics/src/test/java/com/segment/analytics/AnalyticsTest.java index 0a6a1b80b..c81f027aa 100644 --- a/analytics/src/test/java/com/segment/analytics/AnalyticsTest.java +++ b/analytics/src/test/java/com/segment/analytics/AnalyticsTest.java @@ -348,6 +348,21 @@ public void optionsDisableIntegrations() { verifyNoMoreInteractions(integration); } + @Test + public void optionsCustomContext() { + analytics.track("foo", null, new Options().putContext("from_tests", true)); + + verify(integration) + .track( + argThat( + new NoDescriptionMatcher() { + @Override + protected boolean matchesSafely(TrackPayload payload) { + return payload.context().get("from_tests") == Boolean.TRUE; + } + })); + } + @Test public void optOutDisablesEvents() throws IOException { analytics.optOut(true); diff --git a/analytics/src/test/java/com/segment/analytics/OptionsTest.java b/analytics/src/test/java/com/segment/analytics/OptionsTest.java index 34afaf1cf..560993e4a 100644 --- a/analytics/src/test/java/com/segment/analytics/OptionsTest.java +++ b/analytics/src/test/java/com/segment/analytics/OptionsTest.java @@ -79,4 +79,23 @@ public void setIntegration() throws Exception { new ImmutableMap.Builder().put("appId", "bar").build()) .build()); } + + @Test + public void setOptions() { + options.putContext("foo", "bar"); + options.putContext( + "library", + new ImmutableMap.Builder().put("name", "analytics-test").build()); + + assertThat(options.context()) + .isEqualTo( + new ImmutableMap.Builder() + .put("foo", "bar") + .put( + "library", + new ImmutableMap.Builder() + .put("name", "analytics-test") + .build()) + .build()); + } }