();
@@ -464,7 +535,7 @@ public static MixpanelAPI getInstance(Context context, String token, boolean opt
MixpanelAPI instance = instances.get(appContext);
if (null == instance && ConfigurationChecker.checkBasicConfiguration(appContext)) {
- instance = new MixpanelAPI(appContext, sReferrerPrefs, token, optOutTrackingDefault, superProperties, instanceName, trackAutomaticEvents);
+ instance = new MixpanelAPI(appContext, sReferrerPrefs, token, MPConfig.getInstance(context, options.getInstanceName()), options, trackAutomaticEvents);
registerAppLinksListeners(context, instance);
instances.put(appContext, instance);
}
@@ -669,6 +740,7 @@ public void identify(String distinctId, boolean usePeople) {
mPersistentIdentity.setEventsDistinctId(distinctId);
mPersistentIdentity.setAnonymousIdIfAbsent(currentEventsDistinctId);
mPersistentIdentity.markEventsUserIdPresent();
+ mFeatureFlagManager.loadFlags();
try {
JSONObject identifyPayload = new JSONObject();
identifyPayload.put("$anon_distinct_id", currentEventsDistinctId);
@@ -890,6 +962,24 @@ protected String getUserId() {
return mPersistentIdentity.getEventsUserId();
}
+ /**
+ * Retrieves the Mixpanel project token.
+ *
+ * @return The Mixpanel project token currently being used.
+ */
+ public String getToken() {
+ return mToken;
+ }
+
+ /**
+ * Retrieves the Mixpanel configuration object.
+ *
+ * @return The current {@link MPConfig} object containing Mixpanel settings.
+ */
+ public MPConfig getMPConfig() {
+ return mConfig;
+ }
+
/**
* Register properties that will be sent with every subsequent call to {@link #track(String, JSONObject)}.
*
@@ -1181,6 +1271,17 @@ private String makeMapKey(String groupKey, Object groupID) {
return groupKey + '_' + groupID;
}
+ /**
+ * Returns a {@link Flags} object that can be used to retrieve and manage
+ * feature flags from Mixpanel.
+ *
+ * @return an instance of {@link Flags} that allows you to access feature flag
+ * configurations for your project.
+ */
+ public Flags getFlags() {
+ return mFeatureFlagManager;
+ }
+
/**
* Clears tweaks and all distinct_ids, superProperties, and push registrations from persistent storage.
* Will not clear referrer information.
@@ -1658,6 +1759,225 @@ public interface Group {
void deleteGroup();
}
+
+ /**
+ * Core interface for using Mixpanel Feature Flags.
+ * You can get an instance by calling {@link MixpanelAPI#getFlags()} (assuming such a method exists).
+ *
+ * The Flags interface allows you to manage and retrieve feature flags defined in your Mixpanel project.
+ * Feature flags can be used to remotely configure your application's behavior, roll out new features
+ * gradually, or run A/B tests.
+ *
+ *
It's recommended to load flags early in your application's lifecycle, for example,
+ * in your main Application class or main Activity's {@code onCreate} method.
+ *
+ *
A typical use case for the Flags interface might look like this:
+ *
+ *
+ * {@code
+ *
+ * public class MainActivity extends Activity {
+ * MixpanelAPI mMixpanel;
+ * Flags mFlags;
+ *
+ * public void onCreate(Bundle saved) {
+ * super.onCreate(saved);
+ * MixanelOptions mpOptions = new MixpanelOptions.Builder().featureFlagsEnabled(true).build();
+ * mMixpanel = MixpanelAPI.getInstance(this, "YOUR MIXPANEL TOKEN", true, mpOptions);
+ * mFlags = mMixpanel.getFlags();
+ *
+ * // Asynchronously load flags
+ * mFlags.loadFlags();
+ *
+ * // Example of checking a flag asynchronously
+ * mFlags.isFlagEnabled("new-checkout-flow", false, isEnabled -> {
+ * if (isEnabled) {
+ * // Show new checkout flow
+ * } else {
+ * // Show old checkout flow
+ * }
+ * });
+ *
+ * // Example of getting a flag value synchronously after ensuring flags are ready
+ * if (mFlags.areFlagsReady()) {
+ * String buttonLabel = (String) mFlags.getVariantValueSync("home-button-label", "Default Label");
+ * // Use buttonLabel
+ * }
+ * }
+ * }
+ *
+ * }
+ *
+ *
+ * @see MixpanelAPI
+ */
+ public interface Flags {
+
+ // --- Public Methods ---
+
+ /**
+ * Asynchronously loads flags from the Mixpanel server if they haven't been loaded yet
+ * or if the cached flags have expired. This method will initiate a network request
+ * if necessary. Subsequent calls to get flag values (especially asynchronous ones)
+ * may trigger this load if flags are not yet available.
+ */
+ void loadFlags();
+
+ /**
+ * Returns true if flags have been successfully loaded from the server and are
+ * currently available for synchronous access. This is useful to check before
+ * calling synchronous flag retrieval methods like {@link #getVariantSync(String, MixpanelFlagVariant)}
+ * to avoid them returning the fallback value immediately.
+ *
+ * @return true if flags are loaded and ready, false otherwise.
+ */
+ boolean areFlagsReady();
+
+ // --- Sync Flag Retrieval ---
+
+ /**
+ * Gets the complete feature flag data (key and value) synchronously.
+ *
+ * IMPORTANT: This method can block the calling thread if it needs to wait for
+ * flags to be loaded (though the provided implementation detail suggests it returns
+ * fallback immediately if not ready). It is strongly recommended NOT to call this
+ * from the main UI thread if {@link #areFlagsReady()} is false, as it could lead
+ * to ANR (Application Not Responding) issues if blocking were to occur.
+ *
+ *
If flags are not ready (i.e., {@link #areFlagsReady()} returns false), this method
+ * will return the provided {@code fallback} value immediately without attempting to
+ * fetch flags or block.
+ *
+ * @param featureName The unique name (key) of the feature flag to retrieve.
+ * @param fallback The {@link MixpanelFlagVariant} instance to return if the specified
+ * flag is not found in the loaded set, or if flags are not ready.
+ * This must not be null.
+ * @return The {@link MixpanelFlagVariant} for the found feature flag, or the {@code fallback}
+ * if the flag is not found or flags are not ready.
+ */
+ @NonNull
+ MixpanelFlagVariant getVariantSync(@NonNull String featureName, @NonNull MixpanelFlagVariant fallback);
+
+ /**
+ * Gets the value of a specific feature flag synchronously.
+ *
+ *
IMPORTANT: Similar to {@link #getVariantSync(String, MixpanelFlagVariant)}, this method
+ * may involve blocking behavior if flags are being loaded. It's advised to check
+ * {@link #areFlagsReady()} first and avoid calling this on the main UI thread if flags
+ * might not be ready.
+ *
+ *
If flags are not ready, or if the specified {@code featureName} is not found,
+ * this method returns the {@code fallbackValue} immediately.
+ *
+ * @param featureName The unique name (key) of the feature flag.
+ * @param fallbackValue The default value to return if the flag is not found,
+ * its value is null, or if flags are not ready. Can be null.
+ * @return The value of the feature flag (which could be a String, Boolean, Number, etc.),
+ * or the {@code fallbackValue}.
+ */
+ @Nullable
+ Object getVariantValueSync(@NonNull String featureName, @Nullable Object fallbackValue);
+
+ /**
+ * Checks if a specific feature flag is enabled synchronously. A flag is considered
+ * enabled if its value evaluates to {@code true}.
+ *
+ *
+ * - If the flag's value is a Boolean, it's returned directly.
+ * - If the flag's value is a String, it's considered {@code true} if it equals (case-insensitive) "true" or "1".
+ * - If the flag's value is a Number, it's considered {@code true} if it's non-zero.
+ * - For other types, or if the flag is not found, it relies on the {@code fallbackValue}.
+ *
+ *
+ * IMPORTANT: See warnings on {@link #getVariantSync(String, MixpanelFlagVariant)} regarding
+ * potential blocking and the recommendation to check {@link #areFlagsReady()} first,
+ * especially when calling from the main UI thread.
+ *
+ *
Returns {@code fallbackValue} immediately if flags are not ready or the flag is not found.
+ *
+ * @param featureName The unique name (key) of the feature flag.
+ * @param fallbackValue The default boolean value to return if the flag is not found,
+ * cannot be evaluated as a boolean, or if flags are not ready.
+ * @return {@code true} if the flag is present and evaluates to true, otherwise {@code false}
+ * (or the {@code fallbackValue}).
+ */
+ boolean isEnabledSync(@NonNull String featureName, boolean fallbackValue);
+
+ // --- Async Flag Retrieval ---
+
+ /**
+ * Asynchronously gets the complete feature flag data (key and value).
+ *
+ *
If flags are not currently loaded, this method will trigger a fetch from the
+ * Mixpanel server. The provided {@code completion} callback will be invoked on the
+ * main UI thread once the operation is complete.
+ *
+ *
If the fetch fails or the specific flag is not found after a successful fetch,
+ * the {@code fallback} data will be provided to the completion callback.
+ *
+ * @param featureName The unique name (key) of the feature flag to retrieve.
+ * @param fallback The {@link MixpanelFlagVariant} instance to return via the callback
+ * if the flag is not found or if the fetch operation fails.
+ * This must not be null.
+ * @param completion The {@link FlagCompletionCallback} that will be invoked on the main
+ * thread with the result (either the found {@link MixpanelFlagVariant} or
+ * the {@code fallback}). This must not be null.
+ */
+ void getVariant(
+ @NonNull String featureName,
+ @NonNull MixpanelFlagVariant fallback,
+ @NonNull FlagCompletionCallback completion
+ );
+
+ /**
+ * Asynchronously gets the value of a specific feature flag.
+ *
+ * If flags are not currently loaded, this method will trigger a fetch. The
+ * {@code completion} callback is invoked on the main UI thread with the flag's
+ * value or the {@code fallbackValue}.
+ *
+ * @param featureName The unique name (key) of the feature flag.
+ * @param fallbackValue The default value to return via the callback if the flag is
+ * not found, its value is null, or if the fetch operation fails.
+ * Can be null.
+ * @param completion The {@link FlagCompletionCallback} that will be invoked on the main
+ * thread with the result (the flag's value or the {@code fallbackValue}).
+ * This must not be null.
+ */
+ void getVariantValue(
+ @NonNull String featureName,
+ @Nullable Object fallbackValue,
+ @NonNull FlagCompletionCallback