diff --git a/CHANGELOG.md b/CHANGELOG.md
index c1733acfb..4309d5368 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 26.1.2
+* Added `CountlyInitProvider` ContentProvider to register activity lifecycle callbacks before `Application.onCreate()`. This ensures the SDK captures the current activity in single-activity frameworks (Flutter, React Native) and apps with deferred initialization.
+* Added `CountlyConfig.setInitialActivity(Activity)` as an explicit way for wrapper SDKs to provide the host activity during initialization.
+
## 26.1.1
* Added Content feature method `previewContent(String contentId)` (Experimental!).
* Improved content display and refresh mechanics.
diff --git a/gradle.properties b/gradle.properties
index c1475ef77..e9480e0c6 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -22,7 +22,7 @@ org.gradle.configureondemand=true
android.useAndroidX=true
android.enableJetifier=true
# RELEASE FIELD SECTION
-VERSION_NAME=26.1.1
+VERSION_NAME=26.1.2-RC1
GROUP=ly.count.android
POM_URL=https://github.com/Countly/countly-sdk-android
POM_SCM_URL=https://github.com/Countly/countly-sdk-android
diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/TestUtils.java b/sdk/src/androidTest/java/ly/count/android/sdk/TestUtils.java
index 1e7ad7de4..d075efcdb 100644
--- a/sdk/src/androidTest/java/ly/count/android/sdk/TestUtils.java
+++ b/sdk/src/androidTest/java/ly/count/android/sdk/TestUtils.java
@@ -44,7 +44,7 @@ public class TestUtils {
public final static String commonAppKey = "appkey";
public final static String commonDeviceId = "1234";
public final static String SDK_NAME = "java-native-android";
- public final static String SDK_VERSION = "26.1.1";
+ public final static String SDK_VERSION = "26.1.2-RC1";
public static final int MAX_THREAD_COUNT_PER_STACK_TRACE = 50;
public static class Activity2 extends Activity {
diff --git a/sdk/src/main/AndroidManifest.xml b/sdk/src/main/AndroidManifest.xml
index c30a4ee07..e80a441e3 100644
--- a/sdk/src/main/AndroidManifest.xml
+++ b/sdk/src/main/AndroidManifest.xml
@@ -9,6 +9,11 @@
android:taskAffinity=".CountlyPushActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:exported="false"/>
+
+
currentActivity;
+
+ private CountlyActivityHolder() {
+ }
+
+ static CountlyActivityHolder getInstance() {
+ return instance;
+ }
+
+ @Nullable Activity getActivity() {
+ if (currentActivity != null) {
+ return currentActivity.get();
+ }
+ return null;
+ }
+
+ void setActivity(@NonNull Activity activity) {
+ if (currentActivity != null && currentActivity.get() == activity) {
+ return;
+ }
+ currentActivity = new WeakReference<>(activity);
+ }
+
+ void clearActivity(@NonNull Activity activity) {
+ if (currentActivity != null && currentActivity.get() != activity) {
+ return;
+ }
+ currentActivity = null;
+ }
+}
diff --git a/sdk/src/main/java/ly/count/android/sdk/CountlyConfig.java b/sdk/src/main/java/ly/count/android/sdk/CountlyConfig.java
index 3b2131826..b19598235 100644
--- a/sdk/src/main/java/ly/count/android/sdk/CountlyConfig.java
+++ b/sdk/src/main/java/ly/count/android/sdk/CountlyConfig.java
@@ -1,5 +1,6 @@
package ly.count.android.sdk;
+import android.app.Activity;
import android.app.Application;
import android.content.Context;
import java.util.ArrayList;
@@ -175,6 +176,8 @@ public class CountlyConfig {
protected Application application = null;
+ protected Activity initialActivity = null;
+
boolean disableLocation = false;
String locationCountyCode = null;
@@ -845,6 +848,20 @@ public synchronized CountlyConfig setApplication(Application application) {
return this;
}
+ /**
+ * Set the initial activity reference for SDK initialization.
+ * This is needed for frameworks like Flutter and React Native where the host activity
+ * is already started before the SDK registers its lifecycle callbacks.
+ * Setting this ensures that content overlays and feedback widgets can display correctly.
+ *
+ * @param activity the current foreground activity
+ * @return Returns the same config object for convenient linking
+ */
+ public synchronized CountlyConfig setInitialActivity(Activity activity) {
+ this.initialActivity = activity;
+ return this;
+ }
+
/**
* Enable the recording of the app start time
*
diff --git a/sdk/src/main/java/ly/count/android/sdk/CountlyInitProvider.java b/sdk/src/main/java/ly/count/android/sdk/CountlyInitProvider.java
new file mode 100644
index 000000000..099d1b632
--- /dev/null
+++ b/sdk/src/main/java/ly/count/android/sdk/CountlyInitProvider.java
@@ -0,0 +1,97 @@
+package ly.count.android.sdk;
+
+import android.app.Activity;
+import android.app.Application;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * ContentProvider that registers ActivityLifecycleCallbacks before Application.onCreate().
+ * This ensures that the SDK captures the first Activity reference even when Countly.init()
+ * is called after the Activity has already started (e.g., in Flutter, React Native, or
+ * single-activity apps with deferred initialization).
+ *
+ * The captured Activity is stored in {@link CountlyActivityHolder} and used during
+ * SDK initialization to seed modules that need an Activity reference.
+ *
+ * This provider performs no actual content operations.
+ */
+public class CountlyInitProvider extends ContentProvider {
+ @Override
+ public boolean onCreate() {
+ Context context = getContext();
+ if (context == null) {
+ return false;
+ }
+
+ Context appContext = context.getApplicationContext();
+ if (appContext instanceof Application) {
+ ((Application) appContext).registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
+ @Override
+ public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
+ CountlyActivityHolder.getInstance().setActivity(activity);
+ }
+
+ @Override
+ public void onActivityStarted(@NonNull Activity activity) {
+ CountlyActivityHolder.getInstance().setActivity(activity);
+ }
+
+ @Override
+ public void onActivityResumed(@NonNull Activity activity) {
+ CountlyActivityHolder.getInstance().setActivity(activity);
+ }
+
+ @Override
+ public void onActivityPaused(@NonNull Activity activity) {
+ }
+
+ @Override
+ public void onActivityStopped(@NonNull Activity activity) {
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
+ }
+
+ @Override
+ public void onActivityDestroyed(@NonNull Activity activity) {
+ CountlyActivityHolder.getInstance().clearActivity(activity);
+ }
+ });
+ }
+
+ return false;
+ }
+
+ @Nullable @Override
+ public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
+ return null;
+ }
+
+ @Nullable @Override
+ public String getType(@NonNull Uri uri) {
+ return null;
+ }
+
+ @Nullable @Override
+ public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/sdk/src/main/java/ly/count/android/sdk/ModuleBase.java b/sdk/src/main/java/ly/count/android/sdk/ModuleBase.java
index 518b8afde..4945d2840 100644
--- a/sdk/src/main/java/ly/count/android/sdk/ModuleBase.java
+++ b/sdk/src/main/java/ly/count/android/sdk/ModuleBase.java
@@ -59,6 +59,14 @@ void onConfigurationChanged(Configuration newConfig) {
void onActivityStarted(Activity activity, int updatedActivityCount) {
}
+ /**
+ * Called during init when the app is already in the foreground and an initial activity
+ * was provided via CountlyConfig.setInitialActivity(). This only sets the activity
+ * reference without triggering counters, sessions, or view tracking.
+ */
+ void onInitialActivitySeeded(@NonNull Activity activity) {
+ }
+
/**
* Called manually by a countly call from the developer
*/
diff --git a/sdk/src/main/java/ly/count/android/sdk/ModuleContent.java b/sdk/src/main/java/ly/count/android/sdk/ModuleContent.java
index e6f18f610..d9b7f730b 100644
--- a/sdk/src/main/java/ly/count/android/sdk/ModuleContent.java
+++ b/sdk/src/main/java/ly/count/android/sdk/ModuleContent.java
@@ -66,6 +66,15 @@ void initFinished(@NotNull CountlyConfig config) {
}
}
+ @Override
+ void onInitialActivitySeeded(@NonNull Activity activity) {
+ L.d("[ModuleContent] onInitialActivitySeeded, activity: [" + activity.getClass().getSimpleName() + "]");
+ currentActivity = activity;
+ if (UtilsDevice.cutout == null) {
+ UtilsDevice.getCutout(activity);
+ }
+ }
+
@Override
void onActivityStarted(Activity activity, int updatedActivityCount) {
if (activity == null) {
diff --git a/sdk/src/main/java/ly/count/android/sdk/ModuleFeedback.java b/sdk/src/main/java/ly/count/android/sdk/ModuleFeedback.java
index 6c8ee07d2..0b3d40d8a 100644
--- a/sdk/src/main/java/ly/count/android/sdk/ModuleFeedback.java
+++ b/sdk/src/main/java/ly/count/android/sdk/ModuleFeedback.java
@@ -55,6 +55,12 @@ public static class CountlyFeedbackWidget implements Serializable {
feedbackInterface = new Feedback();
}
+ @Override
+ void onInitialActivitySeeded(@NonNull Activity activity) {
+ L.d("[ModuleFeedback] onInitialActivitySeeded, activity: [" + activity.getClass().getSimpleName() + "]");
+ currentActivity = activity;
+ }
+
@Override
void onActivityStarted(Activity activity, int updatedActivityCount) {
if (activity == null) {