callback);
10 | }
11 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/FeatureFlagChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | /**
4 | * Callback interface used for listening to changes to a feature flag.
5 | *
6 | * @see LDClientInterface#registerFeatureFlagListener(String, FeatureFlagChangeListener)
7 | */
8 | @FunctionalInterface
9 | public interface FeatureFlagChangeListener {
10 | /**
11 | * The SDK calls this method when a feature flag value has changed for the current evaluation context.
12 | *
13 | * To obtain the new value, call one of the client methods such as {@link LDClientInterface#boolVariation(String, boolean)}.
14 | *
15 | * @param flagKey the feature flag key
16 | */
17 | void onFeatureFlagChange(String flagKey);
18 | }
19 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/IContextModifier.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | import com.launchdarkly.sdk.LDContext;
4 |
5 | /**
6 | * Modifies contexts when invoked.
7 | */
8 | public interface IContextModifier {
9 |
10 | /**
11 | * Modifies the provided context and returns a resulting context. May result in no changes at
12 | * the discretion of the implementation.
13 | *
14 | * @param context to be modified
15 | * @return another context that is the result of modification
16 | */
17 | LDContext modifyContext(LDContext context);
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDAllFlagsListener.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Callback interface used for listening to changes to the flag store.
7 | */
8 | @FunctionalInterface
9 | public interface LDAllFlagsListener {
10 |
11 | /**
12 | * Called by the SDK whenever it receives an update for the stored flag values of the current context.
13 | *
14 | * @param flagKey A list of flag keys which were created, updated, or deleted as part of the update.
15 | * This list may be empty if the update resulted in no changed flag values.
16 | */
17 | void onChange(List flagKey);
18 |
19 | }
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDFailure.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.google.gson.annotations.JsonAdapter;
6 |
7 | /**
8 | * Container class representing a communication failure with LaunchDarkly servers.
9 | */
10 | @JsonAdapter(LDFailureSerialization.class)
11 | public class LDFailure extends LaunchDarklyException {
12 |
13 | /**
14 | * Enumerated type defining the possible values of {@link LDFailure#getFailureType()}.
15 | */
16 | public enum FailureType {
17 | /**
18 | * A response body received either through polling or streaming was unable to be parsed.
19 | */
20 | INVALID_RESPONSE_BODY,
21 |
22 | /**
23 | * A network request for polling, or the EventSource stream reported a failure.
24 | */
25 | NETWORK_FAILURE,
26 |
27 | /**
28 | * An event was received through the stream with an unknown event key. This could indicate a newer SDK is
29 | * available if new event kinds have become available through the flag stream since the SDK's release.
30 | */
31 | UNEXPECTED_STREAM_ELEMENT_TYPE,
32 |
33 | /**
34 | * This indicates the LDFailure is an instance of LDInvalidResponseCodeFailure.
35 | */
36 | UNEXPECTED_RESPONSE_CODE,
37 |
38 | /**
39 | * Some other issue occurred.
40 | */
41 | UNKNOWN_ERROR
42 | }
43 |
44 | /**
45 | * The failure type
46 | */
47 | @NonNull
48 | private final FailureType failureType;
49 |
50 | /**
51 | * @param message the message
52 | * @param failureType the failure type
53 | */
54 | public LDFailure(String message, @NonNull FailureType failureType) {
55 | super(message);
56 | this.failureType = failureType;
57 | }
58 |
59 | /**
60 | * @param message the message
61 | * @param cause the cause of the failure
62 | * @param failureType the failure type
63 | */
64 | public LDFailure(String message, Throwable cause, @NonNull FailureType failureType) {
65 | super(message, cause);
66 | this.failureType = failureType;
67 | }
68 |
69 | /**
70 | * @return the failure type
71 | */
72 | @NonNull
73 | public FailureType getFailureType() {
74 | return failureType;
75 | }
76 | }
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDFailureSerialization.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | import com.google.gson.JsonDeserializationContext;
4 | import com.google.gson.JsonDeserializer;
5 | import com.google.gson.JsonElement;
6 | import com.google.gson.JsonObject;
7 | import com.google.gson.JsonParseException;
8 | import com.google.gson.JsonSerializationContext;
9 | import com.google.gson.JsonSerializer;
10 |
11 | import java.lang.reflect.Type;
12 |
13 | class LDFailureSerialization implements JsonSerializer, JsonDeserializer {
14 | @Override
15 | public LDFailure deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
16 | JsonObject in = json.getAsJsonObject();
17 | LDFailure.FailureType failureType = context.deserialize(in.get("failureType"), LDFailure.FailureType.class);
18 | String message = in.getAsJsonPrimitive("message").getAsString();
19 | if (failureType == LDFailure.FailureType.UNEXPECTED_RESPONSE_CODE) {
20 | int responseCode = in.getAsJsonPrimitive("responseCode").getAsInt();
21 | boolean retryable = in.getAsJsonPrimitive("retryable").getAsBoolean();
22 | return new LDInvalidResponseCodeFailure(message, responseCode, retryable);
23 | } else {
24 | return new LDFailure(message, failureType);
25 | }
26 | }
27 |
28 | @Override
29 | public JsonElement serialize(LDFailure src, Type typeOfSrc, JsonSerializationContext context) {
30 | if (src == null) {
31 | return null;
32 | }
33 | JsonObject jsonObject = new JsonObject();
34 | jsonObject.add("failureType", context.serialize(src.getFailureType()));
35 | jsonObject.addProperty("message", src.getMessage());
36 | if (src instanceof LDInvalidResponseCodeFailure) {
37 | LDInvalidResponseCodeFailure fail = (LDInvalidResponseCodeFailure) src;
38 | jsonObject.addProperty("responseCode", fail.getResponseCode());
39 | jsonObject.addProperty("retryable", fail.isRetryable());
40 | }
41 | return jsonObject;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDHeaderUpdater.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * An interface to provide the SDK with a function used to modify HTTP headers before each request
7 | * to the LaunchDarkly service.
8 | */
9 | public interface LDHeaderUpdater {
10 | /**
11 | * An application provided method for dynamic configuration of HTTP headers.
12 | *
13 | * @param headers The unmodified headers the SDK prepared for the request
14 | */
15 | void updateHeaders(Map headers);
16 | }
17 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDInvalidResponseCodeFailure.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | import com.google.gson.annotations.JsonAdapter;
4 |
5 | /**
6 | * Container class representing a communication failure with LaunchDarkly servers in which the response was unexpected.
7 | */
8 | @JsonAdapter(LDFailureSerialization.class)
9 | public class LDInvalidResponseCodeFailure extends LDFailure {
10 |
11 | /**
12 | * The response code
13 | */
14 | private final int responseCode;
15 |
16 | /**
17 | * Whether or not the failure may be fixed by retrying
18 | */
19 | private final boolean retryable;
20 |
21 | /**
22 | * @param message the message
23 | * @param responseCode the response code
24 | * @param retryable whether or not retrying may resolve the issue
25 | */
26 | public LDInvalidResponseCodeFailure(String message, int responseCode, boolean retryable) {
27 | super(message, FailureType.UNEXPECTED_RESPONSE_CODE);
28 | this.responseCode = responseCode;
29 | this.retryable = retryable;
30 | }
31 |
32 | /**
33 | * @param message the message
34 | * @param cause the cause of the failure
35 | * @param responseCode the response code
36 | * @param retryable whether or not retrying may resolve the issue
37 | */
38 | public LDInvalidResponseCodeFailure(String message, Throwable cause, int responseCode, boolean retryable) {
39 | super(message, cause, FailureType.UNEXPECTED_RESPONSE_CODE);
40 | this.responseCode = responseCode;
41 | this.retryable = retryable;
42 | }
43 |
44 | /**
45 | * @return true if retrying may resolve the issue
46 | */
47 | public boolean isRetryable() {
48 | return retryable;
49 | }
50 |
51 | /**
52 | * @return the response code
53 | */
54 | public int getResponseCode() {
55 | return responseCode;
56 | }
57 | }
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDPackageConsts.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | /**
4 | * Constants related to the SDK package
5 | */
6 | public class LDPackageConsts {
7 |
8 | /**
9 | * Name of the SDK
10 | */
11 | public static final String SDK_NAME = "android-client-sdk";
12 |
13 | /**
14 | * Name of the platform this SDK is primarily for
15 | */
16 | public static final String SDK_PLATFORM_NAME = "Android";
17 |
18 | /**
19 | * Name that will be used for identifying this SDK when using a network client. An example would be the
20 | * user agent in HTTP.
21 | */
22 | public static final String SDK_CLIENT_NAME = "AndroidClient";
23 |
24 | /**
25 | * Name the logger will use to identify this SDK.
26 | */
27 | public static final String DEFAULT_LOGGER_NAME = "LaunchDarklySdk";
28 | }
29 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDStatusListener.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | /**
4 | * Listener for various SDK state changes.
5 | */
6 | public interface LDStatusListener {
7 |
8 | /**
9 | * Invoked when the connection mode changes
10 | * @param connectionInformation the connection information that gives details about the connection
11 | */
12 | void onConnectionModeChanged(ConnectionInformation connectionInformation);
13 |
14 | /**
15 | * Invoked when an internal issue results in a failure to connect to LaunchDarkly
16 | * @param ldFailure the failure
17 | */
18 | void onInternalFailure(LDFailure ldFailure);
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LaunchDarklyException.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | /**
4 | * Exception class that can be thrown by LaunchDarkly client methods.
5 | */
6 | public class LaunchDarklyException extends Exception {
7 |
8 | /**
9 | * @param message for the exception
10 | */
11 | public LaunchDarklyException(String message) {
12 | super(message);
13 | }
14 |
15 | LaunchDarklyException(String message, Throwable cause) {
16 | super(message, cause);
17 | }
18 | }
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/NoOpContextModifier.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | import com.launchdarkly.sdk.LDContext;
4 |
5 | /**
6 | * Context modifier that does nothing to the context.
7 | */
8 | public class NoOpContextModifier implements IContextModifier {
9 |
10 | @Override
11 | public LDContext modifyContext(LDContext context) {
12 | return context;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/PlatformState.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | import java.io.Closeable;
4 | import java.io.File;
5 |
6 | interface PlatformState extends Closeable {
7 | interface ConnectivityChangeListener {
8 | void onConnectivityChanged(boolean networkAvailable);
9 | }
10 |
11 | interface ForegroundChangeListener {
12 | void onForegroundChanged(boolean foreground);
13 | }
14 |
15 | /**
16 | * Returns true if (as far as the OS knows) the network should be working.
17 | * @return true if the network should be available
18 | */
19 | boolean isNetworkAvailable();
20 |
21 | /**
22 | * Registers a listener to be called if the state of {@link #isNetworkAvailable()}} changes.
23 | * @param listener a listener
24 | */
25 | void addConnectivityChangeListener(ConnectivityChangeListener listener);
26 |
27 | /**
28 | * Undoes the effect of {@link #addConnectivityChangeListener(ConnectivityChangeListener)}. Has
29 | * no effect if no such listener is registered.
30 | * @param listener a listener
31 | */
32 | void removeConnectivityChangeListener(ConnectivityChangeListener listener);
33 |
34 | /**
35 | * Returns true if we believe the application is in the foreground, false if we believe it is in
36 | * the background.
37 | * @return true if in the foreground
38 | */
39 | boolean isForeground();
40 |
41 | /**
42 | * Registers a listener to be called if the state of {@link #isForeground()} changes.
43 | * @param listener a listener
44 | */
45 | void addForegroundChangeListener(ForegroundChangeListener listener);
46 |
47 | /**
48 | * Undoes the effect of {@link #addForegroundChangeListener(ForegroundChangeListener)}.
49 | * @param listener
50 | */
51 | void removeForegroundChangeListener(ForegroundChangeListener listener);
52 |
53 | /**
54 | * Returns the preferred filesystem location for cache files.
55 | * @return a directory path
56 | */
57 | File getCacheDir();
58 | }
59 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/StandardEndpoints.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | import com.launchdarkly.logging.LDLogger;
4 |
5 | import java.net.URI;
6 |
7 | abstract class StandardEndpoints {
8 | private StandardEndpoints() {}
9 |
10 | static final URI DEFAULT_STREAMING_BASE_URI = URI.create("https://clientstream.launchdarkly.com");
11 | static final URI DEFAULT_POLLING_BASE_URI = URI.create("https://clientsdk.launchdarkly.com");
12 | static final URI DEFAULT_EVENTS_BASE_URI = URI.create("https://mobile.launchdarkly.com");
13 |
14 | static final String STREAMING_REQUEST_BASE_PATH = "/meval";
15 | static final String POLLING_REQUEST_GET_BASE_PATH = "/msdk/evalx/contexts";
16 | static final String POLLING_REQUEST_REPORT_BASE_PATH = "/msdk/evalx/context";
17 | static final String ANALYTICS_EVENTS_REQUEST_PATH = "/mobile/events/bulk";
18 | static final String DIAGNOSTIC_EVENTS_REQUEST_PATH = "/mobile/events/diagnostic";
19 |
20 | /**
21 | * Internal method to decide which URI a given component should connect to.
22 | *
23 | * Always returns some URI, falling back on the default if necessary, but logs a warning if we detect that the application
24 | * set some custom endpoints but not this one.
25 | *
26 | * @param serviceEndpointsValue the value set in ServiceEndpoints (this is either the default URI, a custom URI, or null)
27 | * @param defaultValue the constant default URI value defined in StandardEndpoints
28 | * @param description a human-readable string for the type of endpoint being selected, for logging purposes
29 | * @param logger the logger to which we should print the warning, if needed
30 | * @return the base URI we should connect to
31 | */
32 | static URI selectBaseUri(URI serviceEndpointsValue, URI defaultValue, String description, LDLogger logger) {
33 | if (serviceEndpointsValue != null) {
34 | return serviceEndpointsValue;
35 | }
36 | logger.warn("You have set custom ServiceEndpoints without specifying the {} base URI; connections may not work properly", description);
37 | return defaultValue;
38 | }
39 |
40 | /**
41 | * Internal method to determine whether a given base URI was set to a custom value or not.
42 | *
43 | * This boolean value is only used for our diagnostic events. We only check if the value
44 | * differs from the default; if the base URI was "overridden" in configuration, but
45 | * happens to be equal to the default URI, we don't count that as custom
46 | * for the purposes of this diagnostic.
47 | *
48 | * @param serviceEndpointsValue the value set in ServiceEndpoints
49 | * @param defaultValue the constant default URI value defined in StandardEndpoints
50 | * @return true iff the base URI was customized
51 | */
52 | static boolean isCustomBaseUri(URI serviceEndpointsValue, URI defaultValue) {
53 | return serviceEndpointsValue != null && !serviceEndpointsValue.equals(defaultValue);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/TaskExecutor.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | import java.io.Closeable;
4 | import java.util.concurrent.ScheduledFuture;
5 |
6 | /**
7 | * Internal abstraction for standardizing how asynchronous tasks are executed.
8 | */
9 | interface TaskExecutor extends Closeable {
10 | /**
11 | * Causes an action to be performed on the main thread. We use this when we are calling
12 | * application-provided listeners.
13 | *
14 | * If we are already on the main thread, the action is called synchronously. Otherwise, it is
15 | * scheduled to be run asynchronously on the main thread.
16 | *
17 | * @param action the action to execute
18 | */
19 | void executeOnMainThread(Runnable action);
20 |
21 | /**
22 | * Schedules an action to be done asynchronously by a worker. It will not be done on the main
23 | * thread. There are no guarantees as to ordering with other tasks.
24 | *
25 | * @param action the action to execute
26 | * @param delayMillis minimum milliseconds to wait before executing
27 | * @return a ScheduledFuture that can be used to cancel the task
28 | */
29 | ScheduledFuture> scheduleTask(Runnable action, long delayMillis);
30 |
31 | /**
32 | * Schedules an action to be run repeatedly at intervals. It will not be done on the main thread.
33 | *
34 | * @param action the action to execute at each interval
35 | * @param initialDelayMillis milliseconds to wait before the first execution
36 | * @param intervalMillis milliseconds between executions
37 | * @return a ScheduledFuture that can be used to cancel the task
38 | */
39 | ScheduledFuture> startRepeatingTask(Runnable action, long initialDelayMillis, long intervalMillis);
40 | }
41 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/env/ApplicationInfoEnvironmentReporter.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.env;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.launchdarkly.sdk.android.subsystems.ApplicationInfo;
6 |
7 | /**
8 | * An {@link IEnvironmentReporter} that reports the provided {@link ApplicationInfo} for {@link #getApplicationInfo()}
9 | * and defers all other attributes to the next reporter in the chain.
10 | */
11 | class ApplicationInfoEnvironmentReporter extends EnvironmentReporterChainBase implements IEnvironmentReporter {
12 |
13 | private ApplicationInfo applicationInfo;
14 |
15 | public ApplicationInfoEnvironmentReporter(ApplicationInfo applicationInfo) {
16 | this.applicationInfo = applicationInfo;
17 | }
18 |
19 | @NonNull
20 | @Override
21 | public ApplicationInfo getApplicationInfo() {
22 | // defer to super if required property applicationID is missing
23 | if (applicationInfo.getApplicationId() == null) {
24 | return super.getApplicationInfo();
25 | }
26 | return applicationInfo;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/env/EnvironmentReporterBuilder.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.env;
2 |
3 | import android.app.Application;
4 |
5 | import androidx.annotation.Nullable;
6 |
7 | import com.launchdarkly.sdk.android.subsystems.ApplicationInfo;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | /**
13 | * Builder for making an {@link IEnvironmentReporter} with various options.
14 | */
15 | public class EnvironmentReporterBuilder {
16 |
17 | @Nullable
18 | private Application application;
19 |
20 | @Nullable
21 | private ApplicationInfo applicationInfo;
22 |
23 | /**
24 | * Sets the application info that this environment reporter will report when asked in the future,
25 | * overriding the automatically sourced {@link ApplicationInfo}
26 | *
27 | * @param applicationInfo to report.
28 | */
29 | public void setApplicationInfo(ApplicationInfo applicationInfo) {
30 | this.applicationInfo = applicationInfo;
31 | }
32 |
33 | /**
34 | * Enables automatically collecting attributes from the platform.
35 | *
36 | * @param application reference for platform calls
37 | */
38 | public void enableCollectionFromPlatform(Application application) {
39 | this.application = application;
40 | }
41 |
42 | /**
43 | * @return the {@link IEnvironmentReporter}
44 | */
45 | public IEnvironmentReporter build() {
46 | /**
47 | * Create chain of responsibility with the following priority order
48 | * 1. {@link ApplicationInfoEnvironmentReporter} - holds customer override
49 | * 2. {@link AndroidEnvironmentReporter} - Android platform API next
50 | * 3. {@link SDKEnvironmentReporter} - Fallback is SDK constants
51 | */
52 | List reporters = new ArrayList<>();
53 |
54 | if (applicationInfo != null) {
55 | reporters.add(new ApplicationInfoEnvironmentReporter(applicationInfo));
56 | }
57 |
58 | if (application != null) {
59 | reporters.add(new AndroidEnvironmentReporter(application));
60 | }
61 |
62 | // always add fallback reporter
63 | reporters.add(new SDKEnvironmentReporter());
64 |
65 | // build chain of responsibility by iterating on all but last element
66 | for (int i = 0; i < reporters.size() - 1; i++) {
67 | reporters.get(i).setNext(reporters.get(i + 1));
68 | }
69 |
70 | // guaranteed non-empty since fallback reporter is always added
71 | return reporters.get(0);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/env/EnvironmentReporterChainBase.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.env;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.annotation.Nullable;
5 |
6 | import com.launchdarkly.sdk.android.subsystems.ApplicationInfo;
7 |
8 | /**
9 | * Base implementation for using {@link IEnvironmentReporter}s in a chain of responsibility pattern.
10 | */
11 | class EnvironmentReporterChainBase implements IEnvironmentReporter {
12 |
13 | private static final String UNKNOWN = "unknown";
14 |
15 | // the next reporter in the chain if there is one
16 | @Nullable
17 | protected EnvironmentReporterChainBase next;
18 |
19 | public void setNext(EnvironmentReporterChainBase next) {
20 | this.next = next;
21 | }
22 |
23 | @NonNull
24 | @Override
25 | public ApplicationInfo getApplicationInfo() {
26 | return next != null ? next.getApplicationInfo() : new ApplicationInfo(UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN);
27 | }
28 |
29 | @NonNull
30 | @Override
31 | public String getManufacturer() {
32 | return next != null ? next.getManufacturer() : UNKNOWN;
33 | }
34 |
35 | @NonNull
36 | @Override
37 | public String getModel() {
38 | return next != null ? next.getModel() : UNKNOWN;
39 | }
40 |
41 | @NonNull
42 | @Override
43 | public String getLocale() {
44 | return next != null ? next.getLocale() : UNKNOWN;
45 | }
46 |
47 | @NonNull
48 | @Override
49 | public String getOSFamily() {
50 | return next != null ? next.getOSFamily() : UNKNOWN;
51 | }
52 |
53 | @NonNull
54 | @Override
55 | public String getOSName() {
56 | return next != null ? next.getOSName() : UNKNOWN;
57 | }
58 |
59 | @NonNull
60 | @Override
61 | public String getOSVersion() {
62 | return next != null ? next.getOSVersion() : UNKNOWN;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/env/IEnvironmentReporter.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.env;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.launchdarkly.sdk.android.subsystems.ApplicationInfo;
6 |
7 | /**
8 | * Reports information about the software/hardware environment that the SDK is
9 | * executing within.
10 | */
11 | public interface IEnvironmentReporter {
12 |
13 | /**
14 | * @return the {@link ApplicationInfo} for the application environment.
15 | */
16 | @NonNull
17 | ApplicationInfo getApplicationInfo();
18 |
19 | /**
20 | * @return the manufacturer of the device the application is running in
21 | */
22 | @NonNull
23 | String getManufacturer();
24 |
25 | /**
26 | * @return the model of the device the application is running in
27 | */
28 | @NonNull
29 | String getModel();
30 |
31 | /**
32 | * @return a BCP47 language tag representing the locale
33 | */
34 | @NonNull
35 | String getLocale();
36 |
37 | /**
38 | * @return the OS Family that this application is running in
39 | */
40 | @NonNull
41 | String getOSFamily();
42 |
43 | /**
44 | * @return the name of the OS that this application is running in
45 | */
46 | @NonNull
47 | String getOSName();
48 |
49 | /**
50 | * @return the version of the OS that this application is running in
51 | */
52 | @NonNull
53 | String getOSVersion();
54 | }
55 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/env/SDKEnvironmentReporter.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.env;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.launchdarkly.sdk.android.BuildConfig;
6 | import com.launchdarkly.sdk.android.LDPackageConsts;
7 | import com.launchdarkly.sdk.android.subsystems.ApplicationInfo;
8 |
9 | /**
10 | * An {@link IEnvironmentReporter} that reports static SDK information for {@link #getApplicationInfo()}
11 | * and defers all other attributes to the next reporter in the chain.
12 | */
13 | class SDKEnvironmentReporter extends EnvironmentReporterChainBase implements IEnvironmentReporter {
14 |
15 | @NonNull
16 | @Override
17 | public ApplicationInfo getApplicationInfo() {
18 | return new ApplicationInfo(
19 | LDPackageConsts.SDK_NAME,
20 | BuildConfig.VERSION_NAME,
21 | LDPackageConsts.SDK_NAME,
22 | BuildConfig.VERSION_NAME
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/integrations/EvaluationSeriesContext.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.integrations;
2 |
3 | import com.launchdarkly.sdk.LDContext;
4 | import com.launchdarkly.sdk.LDValue;
5 |
6 | import java.util.Map;
7 | import java.util.Objects;
8 |
9 | /**
10 | * Represents parameters associated with a feature flag evaluation. An instance of this class is provided to some
11 | * stages of series of a {@link Hook} implementation. For example, see {@link Hook#beforeEvaluation(EvaluationSeriesContext, Map)}
12 | */
13 | public class EvaluationSeriesContext {
14 |
15 | /**
16 | * The variation method that was used to invoke the evaluation. The stability of this string is not
17 | * guaranteed and should not be used in conditional logic.
18 | */
19 | public final String method;
20 |
21 | /**
22 | * The key of the feature flag being evaluated.
23 | */
24 | public final String flagKey;
25 |
26 | /**
27 | * The context the evaluation was for.
28 | */
29 | public final LDContext context;
30 |
31 | /**
32 | * The user-provided default value for the evaluation.
33 | */
34 | public final LDValue defaultValue;
35 |
36 | /**
37 | * @param method the variation method that was used to invoke the evaluation.
38 | * @param key the key of the feature flag being evaluated.
39 | * @param context the context the evaluation was for.
40 | * @param defaultValue the user-provided default value for the evaluation.
41 | */
42 | public EvaluationSeriesContext(String method, String key, LDContext context, LDValue defaultValue) {
43 | this.flagKey = key;
44 | this.context = context;
45 | this.defaultValue = defaultValue;
46 | this.method = method;
47 | }
48 |
49 | @Override
50 | public boolean equals(Object obj) {
51 | if (this == obj) return true;
52 | if (obj == null || getClass() != obj.getClass()) return false;
53 | EvaluationSeriesContext other = (EvaluationSeriesContext)obj;
54 | return
55 | Objects.equals(method, other.method) &&
56 | Objects.equals(flagKey, other.flagKey) &&
57 | Objects.equals(context, other.context) &&
58 | Objects.equals(defaultValue, other.defaultValue);
59 | }
60 |
61 | @Override
62 | public int hashCode() {
63 | return Objects.hash(method, flagKey, context, defaultValue);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/integrations/HookMetadata.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.integrations;
2 |
3 | /**
4 | * Metadata about the {@link Hook} implementation.
5 | */
6 | public abstract class HookMetadata {
7 |
8 | private final String name;
9 |
10 | public HookMetadata(String name) {
11 | this.name = name;
12 | }
13 |
14 | /**
15 | * @return a friendly name for the {@link Hook} this {@link HookMetadata} belongs to.
16 | */
17 | public String getName() {
18 | return name;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/integrations/HooksConfigurationBuilder.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.integrations;
2 |
3 | import com.launchdarkly.sdk.android.Components;
4 | import com.launchdarkly.sdk.android.subsystems.HookConfiguration;
5 |
6 | import java.util.ArrayList;
7 | import java.util.Collections;
8 | import java.util.List;
9 |
10 | /**
11 | * Contains methods for configuring the SDK's 'hooks'.
12 | *
13 | * If you want to add hooks, use {@link Components#hooks()}, configure accordingly, and pass it
14 | * to {@link com.launchdarkly.sdk.android.LDConfig.Builder#hooks(HooksConfigurationBuilder)}.
15 | *
16 | *
17 | * List hooks = createSomeHooks();
18 | * LDConfig config = new LDConfig.Builder()
19 | * .hooks(
20 | * Components.hooks()
21 | * .setHooks(hooks)
22 | * )
23 | * .build();
24 | *
25 | *
26 | * Note that this class is abstract; the actual implementation is created by calling {@link Components#hooks()}.
27 | */
28 | public abstract class HooksConfigurationBuilder {
29 |
30 | /**
31 | * The current set of hooks the builder has.
32 | */
33 | protected List hooks = Collections.emptyList();
34 |
35 | /**
36 | * Adds the provided list of hooks to the configuration. Note that the order of hooks is important and controls
37 | * the order in which they will be executed. See {@link Hook} for more details.
38 | *
39 | * @param hooks to be added to the configuration
40 | * @return the builder
41 | */
42 | public HooksConfigurationBuilder setHooks(List hooks) {
43 | // copy to avoid list manipulations impacting the SDK
44 | this.hooks = Collections.unmodifiableList(new ArrayList<>(hooks));
45 | return this;
46 | }
47 |
48 | /**
49 | * @return the hooks configuration
50 | */
51 | abstract public HookConfiguration build();
52 | }
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/integrations/IdentifySeriesContext.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.integrations;
2 |
3 | import com.launchdarkly.sdk.LDContext;
4 |
5 | import java.util.Objects;
6 |
7 | /**
8 | * Represents parameters associated with calling identify. An instance of this class is provided to some
9 | * stages of series of a {@link Hook} implementation. For example, see {@link Hook#afterTrack(TrackSeriesContext)}
10 | */
11 | public class IdentifySeriesContext {
12 | /**
13 | * The context associated with the identify operation.
14 | */
15 | public final LDContext context;
16 |
17 | /**
18 | * The timeout, in seconds, associated with the identify operation.
19 | */
20 | public final Integer timeout;
21 |
22 | public IdentifySeriesContext(LDContext context, Integer timeout) {
23 | this.context = context;
24 | this.timeout = timeout;
25 | }
26 |
27 | @Override
28 | public boolean equals(Object obj) {
29 | if (this == obj) return true;
30 | if (obj == null || getClass() != obj.getClass()) return false;
31 | IdentifySeriesContext other = (IdentifySeriesContext)obj;
32 | return
33 | Objects.equals(context, other.context) &&
34 | Objects.equals(timeout, other.timeout);
35 | }
36 |
37 | @Override
38 | public int hashCode() {
39 | return Objects.hash(context, timeout);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/integrations/IdentifySeriesResult.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.integrations;
2 |
3 | import java.util.Objects;
4 |
5 | /**
6 | * The result applies to a single identify operation. An operation may complete
7 | * with an error and then later complete successfully. Only the first completion
8 | * will be executed in the identify series.
9 | *
10 | * For example, a network issue may cause an identify to error since the SDK
11 | * can't refresh its cached data from the cloud at that moment, but then later
12 | * the when the network issue is resolved, the SDK will refresh cached data.
13 | */
14 | public class IdentifySeriesResult {
15 | /**
16 | * The status an identify operation completed with.
17 | *
18 | * An example in which an error may occur is lack of network connectivity
19 | * preventing the SDK from functioning.
20 | */
21 | public enum IdentifySeriesStatus {
22 | COMPLETED,
23 | ERROR
24 | }
25 |
26 | public final IdentifySeriesStatus status;
27 |
28 | public IdentifySeriesResult(IdentifySeriesStatus status) {
29 | this.status = status;
30 | }
31 |
32 | @Override
33 | public boolean equals(Object obj) {
34 | if (this == obj) return true;
35 | if (obj == null || getClass() != obj.getClass()) return false;
36 | IdentifySeriesResult other = (IdentifySeriesResult)obj;
37 | return status == other.status;
38 | }
39 |
40 | @Override
41 | public int hashCode() {
42 | return Objects.hash(status);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/integrations/TrackSeriesContext.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.integrations;
2 |
3 | import com.launchdarkly.sdk.LDContext;
4 | import com.launchdarkly.sdk.LDValue;
5 |
6 | import java.util.Objects;
7 |
8 | /**
9 | * Represents parameters associated with tracking a custom event. An instance of this class is provided to some
10 | * stages of series of a {@link Hook} implementation. For example, see {@link Hook#afterTrack(TrackSeriesContext)}
11 | */
12 | public class TrackSeriesContext {
13 | /**
14 | * The key for the event being tracked.
15 | */
16 | public final String key;
17 |
18 | /**
19 | * The context associated with the track operation.
20 | */
21 | public final LDContext context;
22 |
23 | /**
24 | * The data associated with the track operation.
25 | */
26 | public final LDValue data;
27 |
28 | /**
29 | * The metric value associated with the track operation.
30 | */
31 | public final Double metricValue;
32 |
33 | /**
34 | * @param key the key for the event being tracked.
35 | * @param context the context associated with the track operation.
36 | * @param data the data associated with the track operation.
37 | * @param metricValue the metric value associated with the track operation.
38 | */
39 | public TrackSeriesContext(String key, LDContext context, LDValue data, Double metricValue) {
40 | this.key = key;
41 | this.context = context;
42 | this.data = data;
43 | this.metricValue = metricValue;
44 | }
45 |
46 | @Override
47 | public boolean equals(Object obj) {
48 | if (this == obj) return true;
49 | if (obj == null || getClass() != obj.getClass()) return false;
50 | TrackSeriesContext other = (TrackSeriesContext)obj;
51 | return
52 | Objects.equals(key, other.key) &&
53 | Objects.equals(context, other.context) &&
54 | Objects.equals(data, other.data) &&
55 | Objects.equals(metricValue, other.metricValue);
56 | }
57 |
58 | @Override
59 | public int hashCode() {
60 | return Objects.hash(key, context, data, metricValue);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/integrations/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * This package contains integration tools for connecting the SDK to other software components, or
3 | * configuring how it connects to LaunchDarkly.
4 | */
5 | package com.launchdarkly.sdk.android.integrations;
6 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/interfaces/ServiceEndpoints.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.interfaces;
2 |
3 | import com.launchdarkly.sdk.android.integrations.ServiceEndpointsBuilder;
4 | import java.net.URI;
5 |
6 | /**
7 | * Specifies the base service URIs used by SDK components.
8 | *
9 | * See {@link ServiceEndpointsBuilder} for more details on these properties.
10 | *
11 | * @since 4.0.0
12 | */
13 | public final class ServiceEndpoints {
14 | private final URI streamingBaseUri;
15 | private final URI pollingBaseUri;
16 | private final URI eventsBaseUri;
17 |
18 | /**
19 | * Used internally by the SDK to store service endpoints.
20 | * @param streamingBaseUri the base URI for the streaming service
21 | * @param pollingBaseUri the base URI for the polling service
22 | * @param eventsBaseUri the base URI for the events service
23 | */
24 | public ServiceEndpoints(URI streamingBaseUri, URI pollingBaseUri, URI eventsBaseUri) {
25 | this.streamingBaseUri = streamingBaseUri;
26 | this.pollingBaseUri = pollingBaseUri;
27 | this.eventsBaseUri = eventsBaseUri;
28 | }
29 |
30 | /**
31 | * The base URI for the streaming service.
32 | * @return the base URI, or null
33 | */
34 | public URI getStreamingBaseUri() {
35 | return streamingBaseUri;
36 | }
37 |
38 | /**
39 | * The base URI for the polling service.
40 | * @return the base URI, or null
41 | */
42 | public URI getPollingBaseUri() {
43 | return pollingBaseUri;
44 | }
45 |
46 | /**
47 | * The base URI for the events service.
48 | * @return the base URI, or null
49 | */
50 | public URI getEventsBaseUri() {
51 | return eventsBaseUri;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/interfaces/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Types that are part of the public API, but are not needed for basic use of the SDK.
3 | *
4 | * Types in this namespace include:
5 | *
6 | * - Interfaces that provide a facade for some part of the SDK API.
7 | * - Concrete types that are used as parameters within these interfaces, or as containers for
8 | * pieces of SDK configuration, like {@link com.launchdarkly.sdk.android.interfaces.ServiceEndpoints}.
9 | *
10 | */
11 | package com.launchdarkly.sdk.android.interfaces;
12 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Main package for the LaunchDarkly Android SDK, containing the client and configuration classes.
3 | *
4 | * You will most often use {@link com.launchdarkly.sdk.android.LDClient} (the SDK client) and
5 | * {@link com.launchdarkly.sdk.android.LDConfig} (configuration options for the client).
6 | *
7 | * Other commonly used types such as {@link com.launchdarkly.sdk.LDContext} are in the {@code com.launchdarkly.sdk}
8 | * package, since those are not Android-specific and are shared with the LaunchDarkly Java server-side SDK.
9 | */
10 | package com.launchdarkly.sdk.android;
11 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/ApplicationInfo.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.subsystems;
2 |
3 | import androidx.annotation.Nullable;
4 | import com.launchdarkly.sdk.android.integrations.ApplicationInfoBuilder;
5 |
6 | /**
7 | * Encapsulates the SDK's application metadata.
8 | *
9 | * See {@link ApplicationInfoBuilder} for more details on these properties.
10 | *
11 | * @since 4.1.0
12 | */
13 | public final class ApplicationInfo {
14 |
15 | @Nullable
16 | private final String applicationId;
17 | @Nullable
18 | private final String applicationName;
19 | @Nullable
20 | private final String applicationVersion;
21 | @Nullable
22 | private final String applicationVersionName;
23 |
24 | /**
25 | * Used internally by the SDK to store application metadata.
26 | *
27 | * @param applicationId the application ID
28 | * @param applicationVersion the application version
29 | * @param applicationName friendly name for the application
30 | * @param applicationVersionName friendly name for the version
31 | * @see ApplicationInfoBuilder
32 | */
33 | public ApplicationInfo(String applicationId, String applicationVersion,
34 | String applicationName, String applicationVersionName) {
35 | this.applicationId = applicationId;
36 | this.applicationVersion = applicationVersion;
37 | this.applicationName = applicationName;
38 | this.applicationVersionName = applicationVersionName;
39 | }
40 |
41 | /**
42 | * A unique identifier representing the application where the LaunchDarkly SDK is running.
43 | *
44 | * @return the application identifier, or null
45 | */
46 | @Nullable
47 | public String getApplicationId() {
48 | return applicationId;
49 | }
50 |
51 | /**
52 | * A unique identifier representing the version of the application where the
53 | * LaunchDarkly SDK is running.
54 | *
55 | * @return the application version, or null
56 | */
57 | @Nullable
58 | public String getApplicationVersion() {
59 | return applicationVersion;
60 | }
61 |
62 | /**
63 | * A human friendly name for the application in which the LaunchDarkly SDK is running.
64 | *
65 | * @return the friendly name of the application, or null
66 | */
67 | @Nullable
68 | public String getApplicationName() {
69 | return applicationName;
70 | }
71 |
72 | /**
73 | * A human friendly name for the version of the application in which the LaunchDarkly SDK is running.
74 | *
75 | * @return the friendly name of the version, or null
76 | */
77 | @Nullable
78 | public String getApplicationVersionName() {
79 | return applicationVersionName;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/Callback.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.subsystems;
2 |
3 | /**
4 | * General-purpose interface for callbacks that can succeed or fail.
5 | * @param the return value type
6 | * @since 4.0.0
7 | */
8 | public interface Callback {
9 | /**
10 | * This method is called on successful completion.
11 | * @param result the return value
12 | */
13 | void onSuccess(T result);
14 |
15 | /**
16 | * This method is called on failure.
17 | * @param error the error/exception object
18 | */
19 | void onError(Throwable error);
20 | }
21 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/ComponentConfigurer.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.subsystems;
2 |
3 | /**
4 | * The common interface for SDK component factories and configuration builders. Applications should not
5 | * need to implement this interface.
6 | *
7 | * @param the type of SDK component or configuration object being constructed
8 | * @since 3.3.0
9 | */
10 | public interface ComponentConfigurer {
11 | /**
12 | * Called internally by the SDK to create an implementation instance. Applications should not need
13 | * to call this method.
14 | *
15 | * @param clientContext provides configuration properties and other components from the current
16 | * SDK client instance
17 | * @return a instance of the component type
18 | */
19 | T build(ClientContext clientContext);
20 | }
21 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/DataSource.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.subsystems;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.launchdarkly.sdk.LDContext;
6 | import com.launchdarkly.sdk.android.Components;
7 | import com.launchdarkly.sdk.android.LDConfig;
8 | import com.launchdarkly.sdk.android.LDConfig.Builder;
9 |
10 | import java.io.Closeable;
11 | import java.util.concurrent.Callable;
12 | import java.util.concurrent.Future;
13 |
14 | /**
15 | * Interface for an object that receives updates to feature flags from LaunchDarkly.
16 | *
17 | * This component uses a push model. When it is created, the SDK will provide a reference to a
18 | * {@link DataSourceUpdateSink} component (as part of {@link ClientContext}, which is a write-only
19 | * abstraction of the SDK state. The SDK never requests feature flag data from the
20 | * {@link DataSource}-- it only looks at the last known data that was pushed into the state.
21 | *
22 | * Each {@code LDClient} instance maintains exactly one active data source instance. It stops and
23 | * discards the active data source whenever it needs to create a new one due to a significant state
24 | * change, such as if the evaluation context is changed with {@code identify()}, or if the SDK goes
25 | * online after previously being offline, or if the foreground/background state changes.
26 | *
27 | * @since 3.3.0
28 | * @see Components#streamingDataSource()
29 | * @see Components#pollingDataSource()
30 | * @see LDConfig.Builder#dataSource(ComponentConfigurer)
31 | */
32 | public interface DataSource {
33 | /**
34 | * Initializes the data source. This is called only once per instance.
35 | * @param resultCallback called when the data source has successfully acquired the initial data,
36 | * or if an error has occurred
37 | */
38 | void start(@NonNull Callback resultCallback);
39 |
40 | /**
41 | * Tells the data source to stop.
42 | * @param completionCallback called once it has completely stopped (this is allowed to be
43 | * asynchronous because it might involve network operations that can't
44 | * be done on the main thread)
45 | */
46 | void stop(@NonNull Callback completionCallback);
47 |
48 | /**
49 | * The SDK calls this method to determine whether it needs to stop the current data source and
50 | * start a new one after a state transition.
51 | *
52 | * State transitions include going from foreground to background or vice versa, or changing the
53 | * evaluation context. The SDK will not call this method unless at least one of those types of
54 | * transitions has happened.
55 | *
56 | * If this method returns true, the SDK considers the current data source to be no longer valid,
57 | * stops it, and asks the ComponentConfigurer to create a new one.
58 | *
59 | * If this method returns false, the SDK retains the current data source.
60 | *
61 | * @param newInBackground true if the application is now in the background
62 | * @param newEvaluationContext the new evaluation context
63 | * @return true if the data source should be recreated
64 | * @since 4.1.0
65 | */
66 | default boolean needsRefresh(
67 | boolean newInBackground,
68 | LDContext newEvaluationContext
69 | ) {
70 | return true;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/DataSourceUpdateSink.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.subsystems;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.annotation.Nullable;
5 |
6 | import com.launchdarkly.sdk.LDContext;
7 | import com.launchdarkly.sdk.android.ConnectionInformation;
8 | import com.launchdarkly.sdk.android.DataModel;
9 |
10 | import java.util.Map;
11 |
12 | /**
13 | * Interface that an implementation of {@link DataSource} will use to push data into the SDK.
14 | *
15 | * @since 4.0.0
16 | */
17 | public interface DataSourceUpdateSink {
18 | /**
19 | * Completely overwrites the current contents of the data store with a new set of items.
20 | *
21 | * @param items a map of flag keys to flag evaluation results
22 | */
23 | void init(@NonNull LDContext context, @NonNull Map items);
24 |
25 | /**
26 | * Updates or inserts an item. If an item already exists with the same key, the operation will
27 | * only succeed if the existing version is less than the new version.
28 | *
29 | * If a flag has been deleted, the data source should pass a versioned placeholder created with
30 | * {@link DataModel.Flag#deletedItemPlaceholder(String, int)}.
31 | *
32 | * @param item the new evaluation result data (or a deleted item placeholder)
33 | */
34 | void upsert(@NonNull LDContext context, @NonNull DataModel.Flag item);
35 |
36 | /**
37 | * Informs the SDK of a change in the data source's status or the connection mode.
38 | *
39 | * @param connectionMode the value that should be reported by
40 | * {@link ConnectionInformation#getConnectionMode()}
41 | * @param failure if non-null, represents an error/exception that caused data source
42 | * initialization to fail
43 | */
44 | void setStatus(@NonNull ConnectionInformation.ConnectionMode connectionMode, @Nullable Throwable failure);
45 |
46 | /**
47 | * Informs the SDK that the data source is being permanently shut down due to an unrecoverable
48 | * problem reported by LaunchDarkly, such as the mobile key being invalid.
49 | *
50 | * This implies that the SDK should also stop other components that communicate with
51 | * LaunchDarkly, such as the event processor. It also changes the connection mode to
52 | * {@link com.launchdarkly.sdk.android.ConnectionInformation.ConnectionMode#SHUTDOWN}.
53 | */
54 | void shutDown();
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/DiagnosticDescription.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.subsystems;
2 |
3 | import com.launchdarkly.sdk.LDValue;
4 |
5 | /**
6 | * Optional interface for components to describe their own configuration.
7 | *
8 | * The SDK uses a simplified JSON representation of its configuration when recording diagnostics data.
9 | * Any class that implements {@link ComponentConfigurer} may choose to contribute
10 | * values to this representation, although the SDK may or may not use them. For components that do not
11 | * implement this interface, the SDK may instead describe them using {@code getClass().getSimpleName()}.
12 | *
13 | * The {@link #describeConfiguration(ClientContext)} method should return either null or a JSON value. For
14 | * custom components, the value must be a string that describes the basic nature of this component
15 | * implementation (e.g. "Redis"). Built-in LaunchDarkly components may instead return a JSON object
16 | * containing multiple properties specific to the LaunchDarkly diagnostic schema.
17 | *
18 | * @since 3.3.0
19 | */
20 | public interface DiagnosticDescription {
21 | /**
22 | * Used internally by the SDK to inspect the configuration.
23 | * @param clientContext allows access to the client configuration
24 | * @return an {@link LDValue} or null
25 | */
26 | LDValue describeConfiguration(ClientContext clientContext);
27 | }
28 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/HookConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.subsystems;
2 |
3 | import com.launchdarkly.sdk.android.integrations.HooksConfigurationBuilder;
4 | import com.launchdarkly.sdk.android.integrations.Hook;
5 |
6 | import java.util.Collections;
7 | import java.util.List;
8 |
9 | /**
10 | * Encapsulates the SDK's 'hooks' configuration.
11 | *
12 | * Use {@link HooksConfigurationBuilder} to construct an instance.
13 | */
14 | public class HookConfiguration {
15 |
16 | private final List hooks;
17 |
18 | /**
19 | * @param hooks the list of {@link Hook} that will be registered.
20 | */
21 | public HookConfiguration(List hooks) {
22 | this.hooks = Collections.unmodifiableList(hooks);
23 | }
24 |
25 | /**
26 | * @return an immutable list of hooks
27 | */
28 | public List getHooks() {
29 | return hooks;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/HttpConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.subsystems;
2 |
3 | import com.launchdarkly.sdk.android.LDHeaderUpdater;
4 | import com.launchdarkly.sdk.android.integrations.HttpConfigurationBuilder;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | import static java.util.Collections.emptyMap;
10 |
11 | /**
12 | * Encapsulates top-level HTTP configuration that applies to all SDK components.
13 | *
14 | * Use {@link HttpConfigurationBuilder} to construct an instance.
15 | *
16 | * The SDK's built-in components use OkHttp as the HTTP client implementation, but since OkHttp types
17 | * are not surfaced in the public API and custom components might use some other implementation, this
18 | * class only provides the properties that would be used to create an HTTP client; it does not create
19 | * the client itself. SDK implementation code uses its own helper methods to do so.
20 | *
21 | * @since 3.3.0
22 | */
23 | public final class HttpConfiguration {
24 | private final int connectTimeoutMillis;
25 | private final Map defaultHeaders;
26 | private final LDHeaderUpdater headerTransform;
27 | private final boolean useReport;
28 |
29 | /**
30 | * Creates an instance.
31 | *
32 | * @param connectTimeoutMillis see {@link #getConnectTimeoutMillis()}
33 | * @param defaultHeaders see {@link #getDefaultHeaders()}
34 | * @param headerTransform see {@link #getHeaderTransform()}
35 | * @param useReport see {@link #isUseReport()}
36 | */
37 | public HttpConfiguration(
38 | int connectTimeoutMillis,
39 | Map defaultHeaders,
40 | LDHeaderUpdater headerTransform,
41 | boolean useReport
42 | ) {
43 | super();
44 | this.connectTimeoutMillis = connectTimeoutMillis;
45 | this.defaultHeaders = defaultHeaders == null ? emptyMap() : new HashMap<>(defaultHeaders);
46 | this.headerTransform = headerTransform;
47 | this.useReport = useReport;
48 | }
49 |
50 | /**
51 | * The connection timeout. This is the time allowed for the underlying HTTP client to connect
52 | * to the LaunchDarkly server.
53 | *
54 | * @return the connection timeout in milliseconds
55 | */
56 | public int getConnectTimeoutMillis() {
57 | return connectTimeoutMillis;
58 | }
59 |
60 | /**
61 | * Returns the basic headers that should be added to all HTTP requests from SDK components to
62 | * LaunchDarkly services, based on the current SDK configuration.
63 | *
64 | * @return a list of HTTP header names and values
65 | */
66 | public Iterable> getDefaultHeaders() {
67 | return defaultHeaders.entrySet();
68 | }
69 |
70 | /**
71 | * Returns the callback for modifying request headers, if any.
72 | *
73 | * @return the callback for modifying request headers
74 | */
75 | public LDHeaderUpdater getHeaderTransform() {
76 | return headerTransform;
77 | }
78 |
79 | /**
80 | * The setting for whether to use the HTTP REPORT method.
81 | *
82 | * @return true to use HTTP REPORT
83 | */
84 | public boolean isUseReport() {
85 | return useReport;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/PersistentDataStore.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android.subsystems;
2 |
3 | import java.util.Collection;
4 | import java.util.Map;
5 |
6 | /**
7 | * Interface for a data store that holds feature flag data and other SDK properties in a simple
8 | * string format.
9 | *
10 | * The SDK has a default implementation which uses the Android {@code SharedPreferences} API. A
11 | * custom implementation of this interface could store data somewhere else, or use that API in a
12 | * different way.
13 | *
14 | * Each data item is uniquely identified by the combination of a "namespace" and a "key", and has
15 | * a string value. These are defined as follows:
16 | *
17 | * - Both the namespace and the key are non-empty strings.
18 | * - Both the namespace and the key contain only alphanumeric characters, hyphens, and
19 | * underscores.
20 | * - The value can be any non-null string, including an empty string.
21 | *
22 | *
23 | * The store implementation does not need to worry about adding a LaunchDarkly-specific prefix to
24 | * namespaces to distinguish them from storage that is used for other purposes; the SDK will take
25 | * care of that at a higher level. PersistentDataStore is just a low-level storage mechanism.
26 | *
27 | * The SDK will also provide its own caching layer on top of the persistent data store; the data
28 | * store implementation should not provide caching, but simply do every query or update that the
29 | * SDK tells it to do.
30 | *
31 | * Error handling is defined as follows: if any data store operation encounters an I/O error, or
32 | * is otherwise unable to complete its task, it should throw an exception to make the SDK aware
33 | * of this. The SDK will decide whether to log the exception.
34 | *
35 | * @since 4.0.0
36 | */
37 | public interface PersistentDataStore {
38 | /**
39 | * Attempts to retrieve a string value from the store.
40 | *
41 | * @param storeNamespace the namespace identifier
42 | * @param key the unique key within that namespace
43 | * @return the value, or null if not found
44 | */
45 | String getValue(String storeNamespace, String key);
46 |
47 | /**
48 | * Attempts to update or remove a string value in the store.
49 | *
50 | * @param storeNamespace the namespace identifier
51 | * @param key the unique key within that namespace
52 | * @param value the new value, or null to remove the key
53 | */
54 | void setValue(String storeNamespace, String key, String value);
55 |
56 | /**
57 | * Attempts to update multiple values atomically.
58 | *
59 | * @param storeNamespace the namespace identifier
60 | * @param keysAndValues the keys and values to update
61 | */
62 | void setValues(String storeNamespace, Map keysAndValues);
63 |
64 | /**
65 | * Returns all keys that exist in the namespace.
66 | *
67 | * @param storeNamespace the namespace identifier
68 | * @return the keys
69 | */
70 | Collection getKeys(String storeNamespace);
71 |
72 | /**
73 | * Returns all namespaces that exist in the data store.
74 | *
75 | * This may be an inefficient operation, but the SDK will not call this method on a regular
76 | * basis. It is used only when migrating data from earlier SDK versions.
77 | *
78 | * @return the namespaces
79 | */
80 | Collection getAllNamespaces();
81 |
82 | /**
83 | * Removes any values that currently exist in the given namespace.
84 | *
85 | * @param storeNamespace the namespace identifier
86 | * @param fullyDelete true to purge all data structures related to the namespace, false to
87 | * simply leave it empty
88 | */
89 | void clear(String storeNamespace, boolean fullyDelete);
90 | }
91 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/subsystems/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Interfaces for implementation of LaunchDarkly SDK components.
3 | *
4 | * Most applications will not need to refer to these types. You will use them if you are creating a
5 | * plugin component, such as a database integration. They are also used as interfaces for the built-in
6 | * SDK components, so that plugin components can be used interchangeably with those.
7 | *
8 | * The package also includes concrete types that are used as parameters within these interfaces.
9 | */
10 | package com.launchdarkly.sdk.android.subsystems;
11 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/main/resources/android-logger.properties:
--------------------------------------------------------------------------------
1 | # Android Logger configuration example
2 |
3 | # By default logger will print only ERROR (and higher) messages
4 | # with "MyApplication" tag
5 | #root=ERROR:MyApplication
6 |
7 | # DEBUG (and higher) messages from classes of com.example.database
8 | # will be logged with "MyApplication-Database" tag
9 | logger.com.launchdarkly.eventsource=INFO:EventSource
10 |
11 | # All messages from classes of com.example.ui will be logged with
12 | # "MyApplication-UI" tag
13 | #logger.com.example.ui=MyApplication-UI
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/test/java/android/util/Base64.java:
--------------------------------------------------------------------------------
1 | package android.util;
2 |
3 | // This file exists only to support the unit tests in src/test/java. The issue is that the SDK
4 | // code uses android.util.Base64, which only exists in the Android runtime library; but the unit
5 | // tests (as opposed to the instrumented tests in src/androidTest/java) run against the regular
6 | // Java runtime library. The solution is to put an android.util.Base64 class in the classpath that
7 | // simply delegates to java.util.Base64.
8 | //
9 | // We can't simply change the SDK code to use java.util.Base64 because that is only available in
10 | // Android API 26 and above.
11 |
12 | public class Base64 {
13 | public static String encodeToString(byte[] input, int flags) {
14 | return java.util.Base64.getEncoder().encodeToString(input);
15 | }
16 |
17 | public static byte[] decode(String str, int flags) {
18 | return java.util.Base64.getDecoder().decode(str);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/test/java/android/util/Pair.java:
--------------------------------------------------------------------------------
1 | package android.util;
2 |
3 | // This file exists only to support the unit tests in src/test/java. The issue is that the SDK
4 | // code uses android.util.Pair, which only exists in the Android runtime library; but the unit
5 | // tests (as opposed to the instrumented tests in src/androidTest/java) run against the regular
6 | // Java runtime library. The solution is to put an android.util.Pair class in the classpath that
7 | // is an exact copy of the real android.util.Pair.
8 |
9 | import androidx.annotation.Nullable;
10 |
11 | import java.util.Objects;
12 |
13 | public class Pair {
14 | public final F first;
15 | public final S second;
16 | /**
17 | * Constructor for a Pair.
18 | *
19 | * @param first the first object in the Pair
20 | * @param second the second object in the pair
21 | */
22 | public Pair(F first, S second) {
23 | this.first = first;
24 | this.second = second;
25 | }
26 | /**
27 | * Checks the two objects for equality by delegating to their respective
28 | * {@link Object#equals(Object)} methods.
29 | *
30 | * @param o the {@link Pair} to which this one is to be checked for equality
31 | * @return true if the underlying objects of the Pair are both considered
32 | * equal
33 | */
34 | @Override
35 | public boolean equals(@Nullable Object o) {
36 | if (!(o instanceof Pair)) {
37 | return false;
38 | }
39 | Pair, ?> p = (Pair, ?>) o;
40 | return Objects.equals(p.first, first) && Objects.equals(p.second, second);
41 | }
42 | /**
43 | * Compute a hash code using the hash codes of the underlying objects
44 | *
45 | * @return a hashcode of the Pair
46 | */
47 | @Override
48 | public int hashCode() {
49 | return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
50 | }
51 | @Override
52 | public String toString() {
53 | return "Pair{" + String.valueOf(first) + " " + String.valueOf(second) + "}";
54 | }
55 | /**
56 | * Convenience method for creating an appropriately typed pair.
57 | * @param a the first object in the Pair
58 | * @param b the second object in the pair
59 | * @return a Pair that is templatized with the types of a and b
60 | */
61 | public static Pair create(A a, B b) {
62 | return new Pair(a, b);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/ContextDataManagerContextCachingTest.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | import static com.launchdarkly.sdk.android.AssertHelpers.assertDataSetsEqual;
4 | import static com.launchdarkly.sdk.android.AssertHelpers.assertFlagsEqual;
5 | import static org.junit.Assert.assertEquals;
6 | import static org.junit.Assert.assertNotEquals;
7 | import static org.junit.Assert.assertNotNull;
8 | import static org.junit.Assert.assertNull;
9 | import static org.junit.Assert.assertSame;
10 | import static org.junit.Assert.fail;
11 |
12 | import com.launchdarkly.sdk.LDContext;
13 | import com.launchdarkly.sdk.android.subsystems.PersistentDataStore;
14 |
15 | import org.junit.Rule;
16 | import org.junit.Test;
17 |
18 | public class ContextDataManagerContextCachingTest extends ContextDataManagerTestBase {
19 | @Test
20 | public void deletePreviousDataAfterSwitchForZeroCached() {
21 | ContextDataManager manager = createDataManager(0);
22 |
23 | for (int i = 1; i <= 2; i++) {
24 | manager.initData(makeContext(i), makeFlagData(i));
25 | }
26 |
27 | assertContextIsNotCached(makeContext(1));
28 | }
29 |
30 | @Test
31 | public void canCacheManyContextsWithNegativeMaxCachedContexts() {
32 | ContextDataManager manager = createDataManager(-1);
33 |
34 | int numContexts = 20;
35 | for (int i = 1; i <= numContexts; i++) {
36 | manager.switchToContext(makeContext(i));
37 | manager.initData(makeContext(i), makeFlagData(i));
38 | }
39 |
40 | for (int i = 1; i <= numContexts; i++) {
41 | assertContextIsCached(makeContext(i), makeFlagData(i));
42 | }
43 | assertEquals(numContexts, environmentStore.getIndex().data.size());
44 | }
45 |
46 | @Test
47 | public void deletesExcessContexts() {
48 | int maxCachedContexts = 10, excess = 2;
49 | ContextDataManager manager = createDataManager(maxCachedContexts);
50 |
51 | for (int i = 1; i <= maxCachedContexts + excess; i++) {
52 | manager.switchToContext(makeContext(i));
53 | manager.initData(makeContext(i), makeFlagData(i));
54 | }
55 |
56 | for (int i = 1; i <= excess; i++) {
57 | assertContextIsNotCached(makeContext(i));
58 | }
59 | for (int i = excess + 1; i <= maxCachedContexts + excess; i++) {
60 | assertContextIsCached(makeContext(i), makeFlagData(i));
61 | }
62 | }
63 |
64 | @Test
65 | public void deletesExcessContextsFromPreviousManagerInstance() {
66 | ContextDataManager manager = createDataManager(1);
67 |
68 | for (int i = 1; i <= 2; i++) {
69 | manager.switchToContext(makeContext(i));
70 | manager.initData(makeContext(i), makeFlagData(i));
71 | assertContextIsCached(makeContext(i), makeFlagData(i));
72 | }
73 |
74 | ContextDataManager newManagerInstance = createDataManager(1);
75 | newManagerInstance.switchToContext(makeContext(3));
76 | newManagerInstance.initData(makeContext(3), makeFlagData(3));
77 |
78 | assertContextIsNotCached(makeContext(1));
79 | assertContextIsNotCached(makeContext(2));
80 | assertContextIsCached(makeContext(3), makeFlagData(3));
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/DebounceTest.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | import com.launchdarkly.sdk.android.Debounce;
4 |
5 | import org.junit.Test;
6 |
7 | import static org.junit.Assert.assertEquals;
8 |
9 | public class DebounceTest {
10 |
11 | @Test
12 | public void callPendingNullReturnNoAction() {
13 | Debounce test = new Debounce();
14 | int expected = 0;
15 | int actual = 0;
16 |
17 | test.call(null);
18 |
19 | assertEquals(expected, actual);
20 | }
21 |
22 | @Test
23 | public void callPendingSetReturnOne() throws InterruptedException {
24 | Debounce test = new Debounce();
25 | Integer expected = 1;
26 | final Integer[] actual = {0};
27 |
28 | test.call(() -> {
29 | actual[0] = 1;
30 | return null;
31 | });
32 |
33 | Thread.sleep(1000);
34 |
35 | assertEquals(expected, actual[0]);
36 | }
37 |
38 | @Test
39 | public void callPendingSetReturnTwo() throws InterruptedException {
40 | Debounce test = new Debounce();
41 | Integer expected = 2;
42 | final Integer[] actual = {0};
43 |
44 | test.call(() -> {
45 | actual[0] = 1;
46 | return null;
47 | });
48 | Thread.sleep(1000);
49 | test.call(() -> {
50 | actual[0] = 2;
51 | return null;
52 | });
53 | Thread.sleep(1000);
54 |
55 | assertEquals(expected, actual[0]);
56 | }
57 |
58 | @Test
59 | public void callPendingSetReturnThreeBounce() throws InterruptedException {
60 | Debounce test = new Debounce();
61 | Integer expected = 3;
62 | final Integer[] actual = {0};
63 |
64 | test.call(() -> {
65 | actual[0] = 1;
66 | Thread.sleep(100);
67 | return null;
68 | });
69 | test.call(() -> {
70 | actual[0] = 2;
71 | return null;
72 | });
73 | test.call(() -> {
74 | if (actual[0] == 1)
75 | actual[0] = 3;
76 | return null;
77 | });
78 | Thread.sleep(500);
79 |
80 | assertEquals(expected, actual[0]);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/HttpConfigurationBuilderTest.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | import com.launchdarkly.sdk.android.env.EnvironmentReporterBuilder;
4 | import com.launchdarkly.sdk.android.subsystems.ClientContext;
5 | import com.launchdarkly.sdk.android.subsystems.HttpConfiguration;
6 |
7 | import org.junit.Test;
8 |
9 | import java.util.HashMap;
10 | import java.util.Map;
11 |
12 | import static com.launchdarkly.sdk.android.integrations.HttpConfigurationBuilder.DEFAULT_CONNECT_TIMEOUT_MILLIS;
13 | import static org.junit.Assert.assertEquals;
14 |
15 | public class HttpConfigurationBuilderTest {
16 | private static final String MOBILE_KEY = "mobile-key";
17 | private static final ClientContext BASIC_CONTEXT = new ClientContext(MOBILE_KEY, new EnvironmentReporterBuilder().build(),
18 | null, null, null, "", false, null, null, false, null, null, false);
19 |
20 | private static Map buildBasicHeaders() {
21 | Map ret = new HashMap<>();
22 | ret.put("Authorization", LDUtil.AUTH_SCHEME + MOBILE_KEY);
23 | ret.put("User-Agent", LDUtil.USER_AGENT_HEADER_VALUE);
24 | ret.put("X-LaunchDarkly-Tags", "application-id/" + LDPackageConsts.SDK_NAME + " application-name/" + LDPackageConsts.SDK_NAME
25 | + " application-version/" + BuildConfig.VERSION_NAME + " application-version-name/" + BuildConfig.VERSION_NAME);
26 | return ret;
27 | }
28 |
29 | private static Map toMap(Iterable> entries) {
30 | Map ret = new HashMap<>();
31 | for (Map.Entry e: entries) {
32 | ret.put(e.getKey(), e.getValue());
33 | }
34 | return ret;
35 | }
36 |
37 | @Test
38 | public void testDefaults() {
39 | HttpConfiguration hc = Components.httpConfiguration().build(BASIC_CONTEXT);
40 | assertEquals(DEFAULT_CONNECT_TIMEOUT_MILLIS, hc.getConnectTimeoutMillis());
41 | assertEquals(buildBasicHeaders(), toMap(hc.getDefaultHeaders()));
42 | }
43 |
44 | @Test
45 | public void testConnectTimeout() {
46 | HttpConfiguration hc = Components.httpConfiguration()
47 | .connectTimeoutMillis(999)
48 | .build(BASIC_CONTEXT);
49 | assertEquals(999, hc.getConnectTimeoutMillis());
50 | }
51 |
52 | @Test
53 | public void testWrapperNameOnly() {
54 | HttpConfiguration hc = Components.httpConfiguration()
55 | .wrapper("Scala", null)
56 | .build(BASIC_CONTEXT);
57 | assertEquals("Scala", toMap(hc.getDefaultHeaders()).get("X-LaunchDarkly-Wrapper"));
58 | }
59 |
60 | @Test
61 | public void testWrapperWithVersion() {
62 | HttpConfiguration hc = Components.httpConfiguration()
63 | .wrapper("Scala", "0.1.0")
64 | .build(BASIC_CONTEXT);
65 | assertEquals("Scala/0.1.0", toMap(hc.getDefaultHeaders()).get("X-LaunchDarkly-Wrapper"));
66 | }
67 |
68 | @Test
69 | public void testApplicationTags() {
70 | ClientContext contextWithTags = new ClientContext(MOBILE_KEY, new EnvironmentReporterBuilder().build(),
71 | null, null, null, "", false, null, null, false, null, null, false);
72 | HttpConfiguration hc = Components.httpConfiguration()
73 | .build(contextWithTags);
74 | assertEquals("application-id/" + LDPackageConsts.SDK_NAME + " application-name/" + LDPackageConsts.SDK_NAME
75 | + " application-version/" + BuildConfig.VERSION_NAME + " application-version-name/" + BuildConfig.VERSION_NAME ,
76 | toMap(hc.getDefaultHeaders()).get("X-LaunchDarkly-Tags"));
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/launchdarkly-android-client-sdk/src/test/java/com/launchdarkly/sdk/android/LDCompletedFutureTest.java:
--------------------------------------------------------------------------------
1 | package com.launchdarkly.sdk.android;
2 |
3 | import com.launchdarkly.sdk.android.LDFailedFuture;
4 | import com.launchdarkly.sdk.android.LDSuccessFuture;
5 |
6 | import org.junit.Test;
7 |
8 | import java.util.concurrent.ExecutionException;
9 | import java.util.concurrent.TimeUnit;
10 |
11 | import static org.junit.Assert.assertFalse;
12 | import static org.junit.Assert.assertSame;
13 | import static org.junit.Assert.assertTrue;
14 | import static org.junit.Assert.fail;
15 |
16 | public class LDCompletedFutureTest {
17 | @Test
18 | public void ldSuccessFuture() {
19 | Object contained = new Object();
20 | LDSuccessFuture