diff --git a/.github/workflows/create-release-pr.yml b/.github/workflows/create-release-pr.yml index ec213e71..13e5619e 100644 --- a/.github/workflows/create-release-pr.yml +++ b/.github/workflows/create-release-pr.yml @@ -198,7 +198,7 @@ jobs: cd examples/demo vp run setup cd ios - pod install + pod update OneSignalXCFramework ) echo "✓ Refreshed examples/demo/ios/Podfile.lock" diff --git a/android/build.gradle b/android/build.gradle index 7e32d134..1df305bf 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -27,19 +27,13 @@ android { dependencies { implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}" - // androidx.startup runs our OneSignalInitializer during Application.onCreate so we can register - // an ActivityLifecycleCallbacks before MainActivity.onResume fires. This avoids the cold-start - // race where ReactApplicationContext.getCurrentActivity() returns null and the OneSignal SDK - // ends up holding an ApplicationContext instead of the real Activity. - implementation 'androidx.startup:startup-runtime:1.1.1' - // api is used instead of implementation so the parent :app project can access any of the OneSignal Java // classes if needed. Such as com.onesignal.NotificationExtenderService // // Exclude OkHttp from OneSignal's transitive deps: the otel module pulls in OkHttp 5.x // (via opentelemetry-exporter-sender-okhttp) which is binary-incompatible with React Native's // networking stack (okhttp3.internal.Util removed in 5.x). React Native already provides OkHttp 4.x. - api('com.onesignal:OneSignal:5.9.2') { + api('com.onesignal:OneSignal:5.9.3') { exclude group: 'com.squareup.okhttp3', module: 'okhttp' } diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 899feb92..94cbbcfc 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,14 +1 @@ - - - - - - - + diff --git a/android/src/main/java/com/onesignal/rnonesignalandroid/ActivityLifecycleTracker.java b/android/src/main/java/com/onesignal/rnonesignalandroid/ActivityLifecycleTracker.java deleted file mode 100644 index 4079b943..00000000 --- a/android/src/main/java/com/onesignal/rnonesignalandroid/ActivityLifecycleTracker.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.onesignal.rnonesignalandroid; - -import android.app.Activity; -import android.app.Application; -import android.os.Bundle; -import androidx.annotation.Nullable; -import java.lang.ref.WeakReference; - -/** - * Tracks the host app's current Activity from Application.onCreate onward. - * - *

Registered very early via {@link OneSignalInitializer} (androidx.startup) so it captures the - * first {@code MainActivity.onResume} that fires before the React Native bridge has loaded the JS - * bundle. Without this, {@link com.facebook.react.bridge.ReactApplicationContext#getCurrentActivity()} - * frequently returns {@code null} during cold start in bridgeless mode, causing - * {@code RNOneSignal.initialize} to hand the OneSignal SDK an ApplicationContext instead of the - * real Activity. That in turn leaves {@code ApplicationService.current == null} and queues - * {@code requestPermission()} until the next foreground. - */ -public class ActivityLifecycleTracker implements Application.ActivityLifecycleCallbacks { - private static final ActivityLifecycleTracker INSTANCE = new ActivityLifecycleTracker(); - - private volatile WeakReference currentActivity = new WeakReference<>(null); - - private ActivityLifecycleTracker() {} - - public static ActivityLifecycleTracker getInstance() { - return INSTANCE; - } - - @Nullable - public Activity getCurrentActivity() { - return currentActivity.get(); - } - - @Override - public void onActivityCreated(Activity activity, @Nullable Bundle savedInstanceState) { - currentActivity = new WeakReference<>(activity); - } - - @Override - public void onActivityStarted(Activity activity) { - currentActivity = new WeakReference<>(activity); - } - - @Override - public void onActivityResumed(Activity activity) { - currentActivity = new WeakReference<>(activity); - } - - @Override - public void onActivityPaused(Activity activity) { - // Intentionally no-op: keep the reference so a transient overlay (e.g. permission dialog, - // PermissionsActivity) doesn't blank out the current Activity for callers that race with it. - } - - @Override - public void onActivityStopped(Activity activity) { - // Intentionally no-op for the same reason as onActivityPaused. - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) {} - - @Override - public void onActivityDestroyed(Activity activity) { - Activity current = currentActivity.get(); - if (current == activity) { - currentActivity = new WeakReference<>(null); - } - } -} diff --git a/android/src/main/java/com/onesignal/rnonesignalandroid/OneSignalInitializer.java b/android/src/main/java/com/onesignal/rnonesignalandroid/OneSignalInitializer.java deleted file mode 100644 index 5f295a6a..00000000 --- a/android/src/main/java/com/onesignal/rnonesignalandroid/OneSignalInitializer.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.onesignal.rnonesignalandroid; - -import android.app.Application; -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.startup.Initializer; -import java.util.Collections; -import java.util.List; - -/** - * androidx.startup entry point that registers {@link ActivityLifecycleTracker} against the host - * {@link Application} during {@code Application.onCreate}, before any Activity is created. - * - *

This does NOT initialize the OneSignal SDK itself: the App ID is supplied at runtime by JS - * via {@code OneSignal.initialize(appId)}. The job here is purely to capture the current Activity - * early so that when JS later calls initialize, {@code RNOneSignal} can hand a real Activity to - * {@code OneSignal.initWithContext}. - */ -public class OneSignalInitializer implements Initializer { - - @NonNull - @Override - public ActivityLifecycleTracker create(@NonNull Context context) { - ActivityLifecycleTracker tracker = ActivityLifecycleTracker.getInstance(); - Context appContext = context.getApplicationContext(); - if (appContext instanceof Application) { - ((Application) appContext).registerActivityLifecycleCallbacks(tracker); - } - return tracker; - } - - @NonNull - @Override - public List>> dependencies() { - return Collections.emptyList(); - } -} diff --git a/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java b/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java index 448af37d..ea4e4c61 100644 --- a/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java +++ b/android/src/main/java/com/onesignal/rnonesignalandroid/RNOneSignal.java @@ -230,7 +230,7 @@ public void invalidate() { @Override public void initialize(String appId) { OneSignalWrapper.setSdkType("reactnative"); - OneSignalWrapper.setSdkVersion("050406"); + OneSignalWrapper.setSdkVersion("050407"); if (oneSignalInitDone) { Logging.debug("Already initialized the OneSignal React-Native SDK", null); @@ -238,15 +238,7 @@ public void initialize(String appId) { } ReactApplicationContext reactContext = getReactApplicationContext(); - // Prefer the Activity captured by ActivityLifecycleTracker (registered via androidx.startup - // before MainActivity.onResume), then fall back to ReactApplicationContext's accessor and - // finally the ApplicationContext. Passing the real Activity lets the OneSignal SDK populate - // ApplicationService.current immediately, so requestPermission() can launch the OS dialog - // on the first cold-start instead of waiting for the next foreground event. - Context context = ActivityLifecycleTracker.getInstance().getCurrentActivity(); - if (context == null) { - context = reactContext.getCurrentActivity(); - } + Context context = reactContext.getCurrentActivity(); if (context == null) { context = reactContext.getApplicationContext(); } diff --git a/examples/demo/.gitignore b/examples/demo/.gitignore index 7bc0f3e5..d1b390a3 100644 --- a/examples/demo/.gitignore +++ b/examples/demo/.gitignore @@ -1,5 +1,6 @@ # Environment .env +.rn-demo-env.stamp # OSX # diff --git a/examples/demo/bun.lock b/examples/demo/bun.lock index 575fa1d8..a33228da 100644 --- a/examples/demo/bun.lock +++ b/examples/demo/bun.lock @@ -1006,7 +1006,7 @@ "react-native-dotenv": ["react-native-dotenv@3.4.11", "", { "dependencies": { "dotenv": "^16.4.5" }, "peerDependencies": { "@babel/runtime": "^7.20.6" } }, "sha512-6vnIE+WHABSeHCaYP6l3O1BOEhWxKH6nHAdV7n/wKn/sciZ64zPPp2NUdEUf1m7g4uuzlLbjgr+6uDt89q2DOg=="], - "react-native-onesignal": ["react-native-onesignal@../../react-native-onesignal.tgz", { "dependencies": { "invariant": "^2.2.4" }, "peerDependencies": { "react-native": ">=0.79.0" } }, "sha512-/WxWrib5VMiOxe5OAcGSxZI2ABafJISiQSVZBhYJToA19nSIZj/b1IwBs6PUoZWpUJDdFvYJ4DtD0Vc47OIM/g=="], + "react-native-onesignal": ["react-native-onesignal@../../react-native-onesignal.tgz", { "dependencies": { "invariant": "^2.2.4" }, "peerDependencies": { "react-native": ">=0.79.0" } }, "sha512-rkTUUbWR/k1Xy6YYZCmkQfj7iv0bP/5QEWhYoz9c7p5QDhhfvyHqtsEhY811ARfdjwvoeiPw7HY86ay+M5UqFg=="], "react-native-safe-area-context": ["react-native-safe-area-context@5.7.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-/9/MtQz8ODphjsLdZ+GZAIcC/RtoqW9EeShf7Uvnfgm/pzYrJ75y3PV/J1wuAV1T5Dye5ygq4EAW20RoBq0ABQ=="], diff --git a/examples/setup.sh b/examples/setup.sh index 20e557bb..9f2b6313 100755 --- a/examples/setup.sh +++ b/examples/setup.sh @@ -7,6 +7,7 @@ set -euo pipefail ORIGINAL_DIR=$(pwd) SDK_ROOT="$(cd ../../ && pwd)" STAMP_FILE="$SDK_ROOT/.rn-sdk-source.stamp" +DEMO_ENV_STAMP_FILE="$ORIGINAL_DIR/.rn-demo-env.stamp" TGZ_FILE="$SDK_ROOT/react-native-onesignal.tgz" INSTALLED_DIR="$ORIGINAL_DIR/node_modules/react-native-onesignal" @@ -24,6 +25,34 @@ src_hash=$(find "$SDK_ROOT/src" "$SDK_ROOT/ios" "$SDK_ROOT/android" \ | shasum \ | awk '{print $1}') +demo_env_hash=$( + { + for file in "$ORIGINAL_DIR/.env" "$ORIGINAL_DIR/babel.config.js"; do + if [ -f "$file" ]; then + shasum "$file" + else + echo "missing $file" + fi + done + } | shasum | awk '{print $1}' +) + +if [ ! -f "$DEMO_ENV_STAMP_FILE" ] || [ "$(cat "$DEMO_ENV_STAMP_FILE")" != "$demo_env_hash" ]; then + echo "Demo env inputs changed, clearing Metro cache..." + rm -rf "${TMPDIR:-/tmp}"/metro-* "${TMPDIR:-/tmp}"/haste-map-* "$ORIGINAL_DIR/node_modules/.cache/metro" 2>/dev/null || true + metro_pids=$(lsof -ti tcp:8081 2>/dev/null || true) + for pid in $metro_pids; do + args=$(ps -p "$pid" -o args= 2>/dev/null || true) + case "$args" in + *react-native*|*metro*) + echo "Stopping Metro so @env values are reloaded..." + kill "$pid" 2>/dev/null || true + ;; + esac + done + echo "$demo_env_hash" > "$DEMO_ENV_STAMP_FILE" +fi + # Skip the whole rebuild when: # - the demo already has the SDK installed, # - the cached tarball is still on disk, and diff --git a/ios/RCTOneSignal/RCTOneSignal.mm b/ios/RCTOneSignal/RCTOneSignal.mm index 6a2db226..bc0ba295 100644 --- a/ios/RCTOneSignal/RCTOneSignal.mm +++ b/ios/RCTOneSignal/RCTOneSignal.mm @@ -23,7 +23,7 @@ - (void)initOneSignal:(NSDictionary *)launchOptions { return; OneSignalWrapper.sdkType = @"reactnative"; - OneSignalWrapper.sdkVersion = @"050406"; + OneSignalWrapper.sdkVersion = @"050407"; // initialize the SDK with a nil app ID so cold start click listeners can be // triggered [OneSignal initialize:nil withLaunchOptions:launchOptions]; diff --git a/package.json b/package.json index 061118fb..bbd40177 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-onesignal", - "version": "5.4.6", + "version": "5.4.7", "description": "React Native OneSignal SDK", "keywords": [ "android", diff --git a/react-native-onesignal.podspec b/react-native-onesignal.podspec index cf0ca59f..8bc58883 100644 --- a/react-native-onesignal.podspec +++ b/react-native-onesignal.podspec @@ -15,5 +15,5 @@ Pod::Spec.new do |s| install_modules_dependencies(s) - s.dependency 'OneSignalXCFramework', '5.5.1' + s.dependency 'OneSignalXCFramework', '5.5.2' end