From 8629c9d218e9b09f27e4e199ebc8a3d77e54687f Mon Sep 17 00:00:00 2001 From: sozinov Date: Sun, 24 Aug 2025 18:24:04 +0300 Subject: [PATCH 1/6] WMSDK-445: support auto init for send click data --- android/src/main/AndroidManifest.xml | 7 +- .../com/mindboxsdk/MindboxEventEmitter.kt | 55 ++++++++ .../com/mindboxsdk/MindboxEventSubscriber.kt | 5 + .../com/mindboxsdk/MindboxSdkInitProvider.kt | 63 +++++++++ .../mindboxsdk/MindboxSdkLifecycleEvent.kt | 11 ++ .../mindboxsdk/MindboxSdkLifecycleListener.kt | 129 ++++++++++++++++++ 6 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 android/src/main/java/com/mindboxsdk/MindboxEventEmitter.kt create mode 100644 android/src/main/java/com/mindboxsdk/MindboxEventSubscriber.kt create mode 100644 android/src/main/java/com/mindboxsdk/MindboxSdkInitProvider.kt create mode 100644 android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleEvent.kt create mode 100644 android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index dc01239..9c8a289 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,4 +1,9 @@ - + + + diff --git a/android/src/main/java/com/mindboxsdk/MindboxEventEmitter.kt b/android/src/main/java/com/mindboxsdk/MindboxEventEmitter.kt new file mode 100644 index 0000000..fc939ed --- /dev/null +++ b/android/src/main/java/com/mindboxsdk/MindboxEventEmitter.kt @@ -0,0 +1,55 @@ +package com.mindboxsdk + +import android.app.Activity +import android.app.Application +import android.content.Context +import android.content.Intent +import android.util.Log +import com.facebook.react.ReactApplication +import com.facebook.react.ReactInstanceManager +import com.facebook.react.bridge.ReactContext +import com.facebook.react.ReactActivity +import cloud.mindbox.mobile_sdk.Mindbox +import cloud.mindbox.mobile_sdk.logger.Level + +internal class MindboxEventEmitter ( + private val application: Application +) : MindboxEventSubscriber { + + private var jsDelivery: MindboxJsDelivery? = null + + override fun onEvent(event: MindboxSdkLifecyceEvent) { + when (event) { + is MindboxSdkLifecyceEvent.NewIntent -> handleNewIntent(event.reactContext, event.intent) + is MindboxSdkLifecyceEvent.ActivityCreated -> handleActivityCreated(event.reactContext, event.activity) + is MindboxSdkLifecyceEvent.ActivityDestroyed -> handleActivityDestroyed() + } + } + + private fun handleNewIntent(context: ReactContext, intent: Intent) { + Mindbox.writeLog("[RN] Handle new intent in event emitter. ", Level.INFO) + Mindbox.onNewIntent(intent) + Mindbox.onPushClicked(context, intent) + jsDelivery?.sendPushClicked(intent) + } + + private fun handleActivityCreated(reactContext:ReactContext, activity: Activity) { + Mindbox.writeLog("[RN] Handle activity created", Level.INFO) + runCatching { + reactContext.let { reactContext -> + initializeAndSendIntent(reactContext, activity) + } + } + } + + private fun initializeAndSendIntent(context: ReactContext, activity: Activity) { + Mindbox.writeLog("[RN] Initialize MindboxJsDelivery", Level.INFO) + jsDelivery = MindboxJsDelivery.Shared.getInstance(context) + val currentActivity = context.currentActivity ?: activity + currentActivity.intent?.let { handleNewIntent(context, it) } + } + + private fun handleActivityDestroyed() { + jsDelivery = null + } +} diff --git a/android/src/main/java/com/mindboxsdk/MindboxEventSubscriber.kt b/android/src/main/java/com/mindboxsdk/MindboxEventSubscriber.kt new file mode 100644 index 0000000..b20005f --- /dev/null +++ b/android/src/main/java/com/mindboxsdk/MindboxEventSubscriber.kt @@ -0,0 +1,5 @@ +package com.mindboxsdk + +internal interface MindboxEventSubscriber { + fun onEvent(event: MindboxSdkLifecyceEvent) +} diff --git a/android/src/main/java/com/mindboxsdk/MindboxSdkInitProvider.kt b/android/src/main/java/com/mindboxsdk/MindboxSdkInitProvider.kt new file mode 100644 index 0000000..b4037a3 --- /dev/null +++ b/android/src/main/java/com/mindboxsdk/MindboxSdkInitProvider.kt @@ -0,0 +1,63 @@ +package com.mindboxsdk + +import android.app.Application +import android.content.ContentProvider +import android.content.ContentValues +import android.content.pm.PackageManager +import android.database.Cursor +import android.net.Uri +import android.util.Log + +internal class MindboxSdkInitProvider : ContentProvider() { + + companion object { + private const val AUTO_INIT_ENABLED_KEY = "com.mindbox.sdk.AUTO_INIT_ENABLED" + } + + override fun onCreate(): Boolean { + runCatching { + Log.d("Test", "onCreate initProvider.") + (context?.applicationContext as? Application)?.let { application -> + if (isAutoInitEnabled(application)) { + Log.d("Test", "Automatic initialization is enabled.") + MindboxSdkLifecycleListener.register(application) + } else { + Log.d("Test", "Automatic initialization is disabled.") + } + } + }.onFailure { error -> + Log.e("Test", "Automatic initialization failed", error) + } + return true + } + + private fun isAutoInitEnabled(application: Application): Boolean = + runCatching { + val appInfo = application.packageManager.getApplicationInfo( + application.packageName, + PackageManager.GET_META_DATA + ) + appInfo.metaData + ?.getBoolean(AUTO_INIT_ENABLED_KEY, false) + ?.also { Log.d("Mindbox", "Result of reading mindbox metadata is $it") } + ?: false + }.getOrElse { false } + + override fun query( + uri: Uri, + projection: Array?, + selection: String?, + selectionArgs: Array?, + sortOrder: String? + ): Cursor? = null + + override fun getType(uri: Uri): String? = null + override fun insert(uri: Uri, values: ContentValues?): Uri? = null + override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int = 0 + override fun update( + uri: Uri, + values: ContentValues?, + selection: String?, + selectionArgs: Array? + ): Int = 0 +} diff --git a/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleEvent.kt b/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleEvent.kt new file mode 100644 index 0000000..5abefff --- /dev/null +++ b/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleEvent.kt @@ -0,0 +1,11 @@ +package com.mindboxsdk + +import android.app.Activity +import android.content.Intent +import com.facebook.react.bridge.ReactContext + +internal sealed class MindboxSdkLifecyceEvent { + data class NewIntent(val reactContext: ReactContext, val intent: Intent) : MindboxSdkLifecyceEvent() + data class ActivityCreated(val reactContext: ReactContext, val activity: Activity) : MindboxSdkLifecyceEvent() + data class ActivityDestroyed(val activity: Activity) : MindboxSdkLifecyceEvent() +} diff --git a/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt b/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt new file mode 100644 index 0000000..75ce55a --- /dev/null +++ b/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt @@ -0,0 +1,129 @@ +package com.mindboxsdk + +import android.app.Activity +import android.app.Application +import android.content.Intent +import android.os.Bundle +import android.util.Log +import com.facebook.react.ReactApplication +import com.facebook.react.ReactInstanceManager +import com.facebook.react.bridge.ActivityEventListener +import com.facebook.react.bridge.ReactContext +import com.facebook.react.ReactHost +import java.util.concurrent.atomic.AtomicBoolean + + +internal class MindboxSdkLifecycleListener private constructor( + private val application: Application, + private val subscriber: MindboxEventSubscriber +) : Application.ActivityLifecycleCallbacks { + + companion object { + fun register( + application: Application, + subscriber: MindboxEventSubscriber = MindboxEventEmitter(application) + ) { + val listener = MindboxSdkLifecycleListener(application, subscriber) + application.registerActivityLifecycleCallbacks(listener) + } + } + + private val mainActivityClassName: String? by lazy { getLauncherActivityClassName() } + + private fun getLauncherActivityClassName(): String? { + val pm = application.packageManager + val intent = pm.getLaunchIntentForPackage(application.packageName) + return intent?.component?.className + } + + private fun isMainActivity(activity: Activity): Boolean { + return activity.javaClass.name == mainActivityClassName + } + + private var activityEventListener: ActivityEventListener? = null + + private fun onReactContextAvailable(reactContext: ReactContext, activity: Activity) { + Log.i("Mindbox", "Context avialable") + addActivityEventListener(reactContext) + subscriber.onEvent(MindboxSdkLifecyceEvent.ActivityCreated(reactContext, activity)) + } + + private fun registerReactContextListener( + application: Application, + onReady: (ReactContext) -> Unit + ) { + val reactApplication = application.getReactApplication() ?: return + val reactInstanceManager = getReactInstanceManager() + val reactHost = reactApplication.reactHost + + val wrapperListener = object : ReactInstanceManager.ReactInstanceEventListener { + private val called = AtomicBoolean(false) + override fun onReactContextInitialized(context: ReactContext) { + if (called.compareAndSet(false, true)) { + onReady(context) + } + } + } + + reactInstanceManager?.addReactInstanceEventListener(wrapperListener) + + runCatching { + reactHost?.addReactInstanceEventListener(wrapperListener) + } + } + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + if (!isMainActivity(activity)) return + registerReactContextListener(application) { reactContext -> + onReactContextAvailable(reactContext, activity) + } + } + + private fun addActivityEventListener(reactContext: ReactContext) { + activityEventListener?.let { reactContext.removeActivityEventListener(it) } + + activityEventListener = object : ActivityEventListener { + override fun onNewIntent(intent: Intent?) { + intent ?: return + reactContext.currentActivity + ?.takeIf { isMainActivity(it) } + ?.let { + subscriber.onEvent( + MindboxSdkLifecyceEvent.NewIntent( + reactContext, + intent + ) + ) + } + } + + override fun onActivityResult( + activity: Activity?, requestCode: Int, resultCode: Int, data: Intent? + ) { + } + } + reactContext.addActivityEventListener(activityEventListener) + } + + override fun onActivityDestroyed(activity: Activity) { + if (!isMainActivity(activity)) return + subscriber.onEvent(MindboxSdkLifecyceEvent.ActivityDestroyed(activity)) + getReactInstanceManager() + ?.currentReactContext + ?.removeActivityEventListener(activityEventListener) + activityEventListener = null + } + + override fun onActivityStarted(activity: Activity) {} + override fun onActivityResumed(activity: Activity) {} + override fun onActivityPaused(activity: Activity) {} + override fun onActivityStopped(activity: Activity) {} + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} + + + private fun getReactInstanceManager(): ReactInstanceManager? = + application.getReactApplication()?.reactNativeHost?.reactInstanceManager + + private fun Application.getReactApplication() = this as? ReactApplication + +} From b0235794b93d2804a4c1c5c551ac4c91f203e9bc Mon Sep 17 00:00:00 2001 From: sozinov Date: Mon, 25 Aug 2025 13:58:30 +0300 Subject: [PATCH 2/6] WMSDK-445: change reactHost listener --- .../mindboxsdk/MindboxSdkLifecycleListener.kt | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt b/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt index 75ce55a..4127af2 100644 --- a/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt +++ b/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt @@ -9,8 +9,9 @@ import com.facebook.react.ReactApplication import com.facebook.react.ReactInstanceManager import com.facebook.react.bridge.ActivityEventListener import com.facebook.react.bridge.ReactContext -import com.facebook.react.ReactHost import java.util.concurrent.atomic.AtomicBoolean +import cloud.mindbox.mobile_sdk.Mindbox +import cloud.mindbox.mobile_sdk.logger.Level internal class MindboxSdkLifecycleListener private constructor( @@ -43,7 +44,7 @@ internal class MindboxSdkLifecycleListener private constructor( private var activityEventListener: ActivityEventListener? = null private fun onReactContextAvailable(reactContext: ReactContext, activity: Activity) { - Log.i("Mindbox", "Context avialable") + Mindbox.writeLog("[RN] ReactContext ready", Level.INFO) addActivityEventListener(reactContext) subscriber.onEvent(MindboxSdkLifecyceEvent.ActivityCreated(reactContext, activity)) } @@ -54,7 +55,6 @@ internal class MindboxSdkLifecycleListener private constructor( ) { val reactApplication = application.getReactApplication() ?: return val reactInstanceManager = getReactInstanceManager() - val reactHost = reactApplication.reactHost val wrapperListener = object : ReactInstanceManager.ReactInstanceEventListener { private val called = AtomicBoolean(false) @@ -66,14 +66,18 @@ internal class MindboxSdkLifecycleListener private constructor( } reactInstanceManager?.addReactInstanceEventListener(wrapperListener) - - runCatching { - reactHost?.addReactInstanceEventListener(wrapperListener) - } + addReactHostListener(application, wrapperListener) } override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { if (!isMainActivity(activity)) return + + getReactInstanceManager()?.currentReactContext?.let { + onReactContextAvailable(it, activity) + Mindbox.writeLog("[RN] ReactContext already available; skipping listener registration ", Level.INFO) + return + } + registerReactContextListener(application) { reactContext -> onReactContextAvailable(reactContext, activity) } @@ -126,4 +130,34 @@ internal class MindboxSdkLifecycleListener private constructor( private fun Application.getReactApplication() = this as? ReactApplication + private fun addReactHostListener( + application: Application, + wrapperListener: ReactInstanceManager.ReactInstanceEventListener + ) { + runCatching { + val reactApplication = application as? ReactApplication ?: return@runCatching + + val hostClass = Class.forName("com.facebook.react.ReactHost") + val listenerClass = Class.forName("com.facebook.react.ReactInstanceEventListener") + + val addMethod = hostClass.getMethod("addReactInstanceEventListener", listenerClass) + val getHostMethod = reactApplication.javaClass.getMethod("getReactHost") + val reactHost = getHostMethod.invoke(reactApplication) + + val proxy = java.lang.reflect.Proxy.newProxyInstance( + listenerClass.classLoader, + arrayOf(listenerClass) + ) { _, method, args -> + if (method.name == "onReactContextInitialized" && args?.size == 1 && args[0] is ReactContext) { + wrapperListener.onReactContextInitialized(args[0] as ReactContext) + } + null + } + + addMethod.invoke(reactHost, proxy) + Mindbox.writeLog("[RN] success added react context listener for reactHost", Level.INFO) + }.onFailure { + Mindbox.writeLog("[RN] failed added react context listener for reactHost ", Level.INFO) + } + } } From 40ebabd60198ccbe0f5e81a600dffd29802ff5e3 Mon Sep 17 00:00:00 2001 From: sozinov Date: Mon, 25 Aug 2025 15:00:10 +0300 Subject: [PATCH 3/6] WMSDK-445: review --- .../main/java/com/mindboxsdk/MindboxEventEmitter.kt | 8 ++++---- .../main/java/com/mindboxsdk/MindboxEventSubscriber.kt | 2 +- .../main/java/com/mindboxsdk/MindboxSdkInitProvider.kt | 10 +++++----- .../java/com/mindboxsdk/MindboxSdkLifecycleEvent.kt | 8 ++++---- .../java/com/mindboxsdk/MindboxSdkLifecycleListener.kt | 10 +++++----- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/android/src/main/java/com/mindboxsdk/MindboxEventEmitter.kt b/android/src/main/java/com/mindboxsdk/MindboxEventEmitter.kt index fc939ed..5a6de8e 100644 --- a/android/src/main/java/com/mindboxsdk/MindboxEventEmitter.kt +++ b/android/src/main/java/com/mindboxsdk/MindboxEventEmitter.kt @@ -18,11 +18,11 @@ internal class MindboxEventEmitter ( private var jsDelivery: MindboxJsDelivery? = null - override fun onEvent(event: MindboxSdkLifecyceEvent) { + override fun onEvent(event: MindboxSdkLifecycleEvent) { when (event) { - is MindboxSdkLifecyceEvent.NewIntent -> handleNewIntent(event.reactContext, event.intent) - is MindboxSdkLifecyceEvent.ActivityCreated -> handleActivityCreated(event.reactContext, event.activity) - is MindboxSdkLifecyceEvent.ActivityDestroyed -> handleActivityDestroyed() + is MindboxSdkLifecycleEvent.NewIntent -> handleNewIntent(event.reactContext, event.intent) + is MindboxSdkLifecycleEvent.ActivityCreated -> handleActivityCreated(event.reactContext, event.activity) + is MindboxSdkLifecycleEvent.ActivityDestroyed -> handleActivityDestroyed() } } diff --git a/android/src/main/java/com/mindboxsdk/MindboxEventSubscriber.kt b/android/src/main/java/com/mindboxsdk/MindboxEventSubscriber.kt index b20005f..ad7105f 100644 --- a/android/src/main/java/com/mindboxsdk/MindboxEventSubscriber.kt +++ b/android/src/main/java/com/mindboxsdk/MindboxEventSubscriber.kt @@ -1,5 +1,5 @@ package com.mindboxsdk internal interface MindboxEventSubscriber { - fun onEvent(event: MindboxSdkLifecyceEvent) + fun onEvent(event: MindboxSdkLifecycleEvent) } diff --git a/android/src/main/java/com/mindboxsdk/MindboxSdkInitProvider.kt b/android/src/main/java/com/mindboxsdk/MindboxSdkInitProvider.kt index b4037a3..5cb750e 100644 --- a/android/src/main/java/com/mindboxsdk/MindboxSdkInitProvider.kt +++ b/android/src/main/java/com/mindboxsdk/MindboxSdkInitProvider.kt @@ -16,17 +16,17 @@ internal class MindboxSdkInitProvider : ContentProvider() { override fun onCreate(): Boolean { runCatching { - Log.d("Test", "onCreate initProvider.") + Log.i("MindboxSdkInitProvider", "onCreate initProvider.") (context?.applicationContext as? Application)?.let { application -> if (isAutoInitEnabled(application)) { - Log.d("Test", "Automatic initialization is enabled.") + Log.i("MindboxSdkInitProvider", "Automatic initialization is enabled.") MindboxSdkLifecycleListener.register(application) } else { - Log.d("Test", "Automatic initialization is disabled.") + Log.i("MindboxSdkInitProvider", "Automatic initialization is disabled.") } } }.onFailure { error -> - Log.e("Test", "Automatic initialization failed", error) + Log.e("MindboxSdkInitProvider", "Automatic initialization failed", error) } return true } @@ -39,7 +39,7 @@ internal class MindboxSdkInitProvider : ContentProvider() { ) appInfo.metaData ?.getBoolean(AUTO_INIT_ENABLED_KEY, false) - ?.also { Log.d("Mindbox", "Result of reading mindbox metadata is $it") } + ?.also { Log.i("Mindbox", "Result of reading mindbox metadata is $it") } ?: false }.getOrElse { false } diff --git a/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleEvent.kt b/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleEvent.kt index 5abefff..2cce9cf 100644 --- a/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleEvent.kt +++ b/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleEvent.kt @@ -4,8 +4,8 @@ import android.app.Activity import android.content.Intent import com.facebook.react.bridge.ReactContext -internal sealed class MindboxSdkLifecyceEvent { - data class NewIntent(val reactContext: ReactContext, val intent: Intent) : MindboxSdkLifecyceEvent() - data class ActivityCreated(val reactContext: ReactContext, val activity: Activity) : MindboxSdkLifecyceEvent() - data class ActivityDestroyed(val activity: Activity) : MindboxSdkLifecyceEvent() +internal sealed class MindboxSdkLifecycleEvent { + data class NewIntent(val reactContext: ReactContext, val intent: Intent) : MindboxSdkLifecycleEvent() + data class ActivityCreated(val reactContext: ReactContext, val activity: Activity) : MindboxSdkLifecycleEvent() + data class ActivityDestroyed(val activity: Activity) : MindboxSdkLifecycleEvent() } diff --git a/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt b/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt index 4127af2..805962b 100644 --- a/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt +++ b/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt @@ -46,7 +46,7 @@ internal class MindboxSdkLifecycleListener private constructor( private fun onReactContextAvailable(reactContext: ReactContext, activity: Activity) { Mindbox.writeLog("[RN] ReactContext ready", Level.INFO) addActivityEventListener(reactContext) - subscriber.onEvent(MindboxSdkLifecyceEvent.ActivityCreated(reactContext, activity)) + subscriber.onEvent(MindboxSdkLifecycleEvent.ActivityCreated(reactContext, activity)) } private fun registerReactContextListener( @@ -93,7 +93,7 @@ internal class MindboxSdkLifecycleListener private constructor( ?.takeIf { isMainActivity(it) } ?.let { subscriber.onEvent( - MindboxSdkLifecyceEvent.NewIntent( + MindboxSdkLifecycleEvent.NewIntent( reactContext, intent ) @@ -111,7 +111,7 @@ internal class MindboxSdkLifecycleListener private constructor( override fun onActivityDestroyed(activity: Activity) { if (!isMainActivity(activity)) return - subscriber.onEvent(MindboxSdkLifecyceEvent.ActivityDestroyed(activity)) + subscriber.onEvent(MindboxSdkLifecycleEvent.ActivityDestroyed(activity)) getReactInstanceManager() ?.currentReactContext ?.removeActivityEventListener(activityEventListener) @@ -135,7 +135,7 @@ internal class MindboxSdkLifecycleListener private constructor( wrapperListener: ReactInstanceManager.ReactInstanceEventListener ) { runCatching { - val reactApplication = application as? ReactApplication ?: return@runCatching + val reactApplication = application as ReactApplication val hostClass = Class.forName("com.facebook.react.ReactHost") val listenerClass = Class.forName("com.facebook.react.ReactInstanceEventListener") @@ -157,7 +157,7 @@ internal class MindboxSdkLifecycleListener private constructor( addMethod.invoke(reactHost, proxy) Mindbox.writeLog("[RN] success added react context listener for reactHost", Level.INFO) }.onFailure { - Mindbox.writeLog("[RN] failed added react context listener for reactHost ", Level.INFO) + Mindbox.writeLog("[RN] failed added react context listener for reactHost ", Level.ERROR) } } } From ae4ecd6575b1bad2031e25c4353111b77663d202 Mon Sep 17 00:00:00 2001 From: sozinov Date: Mon, 25 Aug 2025 17:57:05 +0300 Subject: [PATCH 4/6] WMSDK-445: added proguard rules --- android/build.gradle | 1 + android/consumer-rules.pro | 5 +++++ .../java/com/mindboxsdk/MindboxSdkLifecycleListener.kt | 7 +++++++ 3 files changed, 13 insertions(+) create mode 100644 android/consumer-rules.pro diff --git a/android/build.gradle b/android/build.gradle index 6930a00..25d6e32 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -33,6 +33,7 @@ android { targetSdkVersion getExtOrIntegerDefault('targetSdkVersion') versionCode 1 versionName "1.0" + consumerProguardFiles 'consumer-rules.pro' } buildTypes { diff --git a/android/consumer-rules.pro b/android/consumer-rules.pro new file mode 100644 index 0000000..70d3596 --- /dev/null +++ b/android/consumer-rules.pro @@ -0,0 +1,5 @@ +-keep class com.facebook.react.ReactHost { *; } +-keep interface com.facebook.react.ReactInstanceEventListener { *; } +-keep interface com.facebook.react.ReactApplication { + public com.facebook.react.ReactHost getReactHost(); +} diff --git a/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt b/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt index 805962b..89f2db5 100644 --- a/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt +++ b/android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleListener.kt @@ -66,6 +66,13 @@ internal class MindboxSdkLifecycleListener private constructor( } reactInstanceManager?.addReactInstanceEventListener(wrapperListener) + // RN 0.78+ introduced ReactHost.addReactInstanceEventListener(...). + // Older RN versions (<= 0.74) expose only ReactInstanceManager.addReactInstanceEventListener(...). + // In New Architecture the ReactInstanceManager listener might not fire + // To support RN 0.78+ reliably while keeping backward compatibility, + // we try to register via ReactHost using reflection (no compile-time dependency). + // If ReactHost API is unavailable (older RN), this call is silently ignored and we rely on + // the ReactInstanceManager path. addReactHostListener(application, wrapperListener) } From 44588145eb2f0d90eb3c4f5a0b433876603a214b Mon Sep 17 00:00:00 2001 From: sozinov Date: Tue, 26 Aug 2025 10:51:21 +0300 Subject: [PATCH 5/6] WMSDK-445: follow review --- .../java/com/mindboxsdk/MindboxSdkInitProvider.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/com/mindboxsdk/MindboxSdkInitProvider.kt b/android/src/main/java/com/mindboxsdk/MindboxSdkInitProvider.kt index 5cb750e..23f8633 100644 --- a/android/src/main/java/com/mindboxsdk/MindboxSdkInitProvider.kt +++ b/android/src/main/java/com/mindboxsdk/MindboxSdkInitProvider.kt @@ -12,21 +12,22 @@ internal class MindboxSdkInitProvider : ContentProvider() { companion object { private const val AUTO_INIT_ENABLED_KEY = "com.mindbox.sdk.AUTO_INIT_ENABLED" + private const val TAG = "MindboxSdkInitProvider" } override fun onCreate(): Boolean { runCatching { - Log.i("MindboxSdkInitProvider", "onCreate initProvider.") + Log.i(TAG, "onCreate initProvider.") (context?.applicationContext as? Application)?.let { application -> if (isAutoInitEnabled(application)) { - Log.i("MindboxSdkInitProvider", "Automatic initialization is enabled.") + Log.i(TAG, "Automatic initialization is enabled.") MindboxSdkLifecycleListener.register(application) } else { - Log.i("MindboxSdkInitProvider", "Automatic initialization is disabled.") + Log.i(TAG, "Automatic initialization is disabled.") } } }.onFailure { error -> - Log.e("MindboxSdkInitProvider", "Automatic initialization failed", error) + Log.e(TAG, "Automatic initialization failed", error) } return true } @@ -39,7 +40,7 @@ internal class MindboxSdkInitProvider : ContentProvider() { ) appInfo.metaData ?.getBoolean(AUTO_INIT_ENABLED_KEY, false) - ?.also { Log.i("Mindbox", "Result of reading mindbox metadata is $it") } + ?.also { Log.i(TAG, "Result of reading mindbox metadata is $it") } ?: false }.getOrElse { false } From b44ed40eb96ca6e3a6f3bfbf6dd175e821562643 Mon Sep 17 00:00:00 2001 From: sozinov Date: Tue, 26 Aug 2025 12:15:34 +0300 Subject: [PATCH 6/6] WMSDK-445: update example --- .../android/app/src/main/AndroidManifest.xml | 4 ++++ .../src/main/java/com/exampleapp/MainActivity.kt | 14 +++++++------- .../main/java/com/exampleapp/MainApplication.kt | 7 +++++++ example/exampleApp/android/build.gradle | 7 ++++--- example/exampleApp/package.json | 8 +++++--- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/example/exampleApp/android/app/src/main/AndroidManifest.xml b/example/exampleApp/android/app/src/main/AndroidManifest.xml index 3152724..5d112d5 100644 --- a/example/exampleApp/android/app/src/main/AndroidManifest.xml +++ b/example/exampleApp/android/app/src/main/AndroidManifest.xml @@ -45,5 +45,9 @@ + + diff --git a/example/exampleApp/android/app/src/main/java/com/exampleapp/MainActivity.kt b/example/exampleApp/android/app/src/main/java/com/exampleapp/MainActivity.kt index 5ab883f..790922f 100644 --- a/example/exampleApp/android/app/src/main/java/com/exampleapp/MainActivity.kt +++ b/example/exampleApp/android/app/src/main/java/com/exampleapp/MainActivity.kt @@ -13,7 +13,7 @@ import com.facebook.react.defaults.DefaultReactActivityDelegate import com.mindboxsdk.MindboxJsDelivery class MainActivity : ReactActivity() { - private var mJsDelivery: MindboxJsDelivery? = null + private var jsDelivery: MindboxJsDelivery? = null override fun getMainComponentName(): String = "exampleApp" override fun createReactActivityDelegate(): ReactActivityDelegate = @@ -22,7 +22,7 @@ class MainActivity : ReactActivity() { // Initializes MindboxJsDelivery and sends the current intent to React Native // https://developers.mindbox.ru/docs/flutter-push-navigation-react-native private fun initializeAndSentIntent(context: ReactContext) { - mJsDelivery = MindboxJsDelivery.Shared.getInstance(context) + jsDelivery = MindboxJsDelivery.Shared.getInstance(context) if (context.hasCurrentActivity()) { sendIntent(context, context.getCurrentActivity()!!.getIntent()) } else { @@ -32,8 +32,8 @@ class MainActivity : ReactActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val mReactInstanceManager = getReactNativeHost().getReactInstanceManager(); - val reactContext = mReactInstanceManager.getCurrentReactContext(); + val reactInstanceManager = getReactNativeHost().getReactInstanceManager(); + val reactContext = reactInstanceManager.getCurrentReactContext(); // Initialize and send intent if React context is already available // https://developers.mindbox.ru/docs/flutter-push-navigation-react-native @@ -41,11 +41,11 @@ class MainActivity : ReactActivity() { initializeAndSentIntent(reactContext); } else { // Add listener to initialize and send intent once React context is initialized - mReactInstanceManager.addReactInstanceEventListener(object : + reactInstanceManager.addReactInstanceEventListener(object : ReactInstanceManager.ReactInstanceEventListener { override fun onReactContextInitialized(context: ReactContext) { initializeAndSentIntent(context) - mReactInstanceManager.removeReactInstanceEventListener(this) + reactInstanceManager.removeReactInstanceEventListener(this) } }) } @@ -63,6 +63,6 @@ class MainActivity : ReactActivity() { Mindbox.onNewIntent(intent) //send click action Mindbox.onPushClicked(context, intent) - mJsDelivery?.sendPushClicked(intent); + jsDelivery?.sendPushClicked(intent); } } diff --git a/example/exampleApp/android/app/src/main/java/com/exampleapp/MainApplication.kt b/example/exampleApp/android/app/src/main/java/com/exampleapp/MainApplication.kt index d156ddb..7436ac3 100644 --- a/example/exampleApp/android/app/src/main/java/com/exampleapp/MainApplication.kt +++ b/example/exampleApp/android/app/src/main/java/com/exampleapp/MainApplication.kt @@ -21,6 +21,9 @@ import com.facebook.react.modules.core.DeviceEventManagerModule import com.google.gson.Gson import com.google.gson.reflect.TypeToken import cloud.mindbox.mindbox_rustore.MindboxRuStore +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load +import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost +import com.facebook.react.defaults.DefaultReactNativeHost class MainApplication : Application(), ReactApplication { @@ -39,6 +42,10 @@ class MainApplication : Application(), ReactApplication { //The fifth step of https://developers.mindbox.ru/docs/firebase-send-push-notifications-react-native Mindbox.initPushServices(this, listOf(MindboxFirebase, MindboxHuawei, MindboxRuStore)) SoLoader.init(this, false) + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + // If you opted-in for the New Architecture, we load the native entry point for this app. + load() + } } private val gson = Gson() diff --git a/example/exampleApp/android/build.gradle b/example/exampleApp/android/build.gradle index 52f2df7..d2f118c 100644 --- a/example/exampleApp/android/build.gradle +++ b/example/exampleApp/android/build.gradle @@ -2,10 +2,11 @@ buildscript { ext { buildToolsVersion = "34.0.0" minSdkVersion = 23 - compileSdkVersion = 34 - targetSdkVersion = 34 - ndkVersion = "25.1.8937393" + compileSdkVersion = 35 + targetSdkVersion = 35 + ndkVersion = "26.1.10909125" kotlinVersion = "1.8.0" + cmakeVersion = "3.22.1" } repositories { google() diff --git a/example/exampleApp/package.json b/example/exampleApp/package.json index 8d6e6b4..fcedece 100644 --- a/example/exampleApp/package.json +++ b/example/exampleApp/package.json @@ -7,6 +7,8 @@ "ios": "react-native run-ios", "lint": "eslint .", "start": "react-native start", + "wipe": "rm -rf ./node_modules && rm -f yarn.lock && rm -f yarn-error.log && cd ./ios && rm -rf ./Pods && rm -f Podfile.lock && cd ../android && rm -rf ./build && rm -rf ./.gradle && cd ./app && rm -rf ./build && cd ../../", + "assemble-debug": "mkdir -p android/app/src/main/assets && npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res/ && cd android && ./gradlew assembleDebug", "test": "jest" }, "dependencies": { @@ -16,10 +18,10 @@ "mindbox-sdk": "^2.13.1", "react": "18.2.0", "react-native": "0.74.0", - "react-native-gesture-handler": "^2.21.2", - "react-native-permissions": "^5.0.0", + "react-native-gesture-handler": "2.21.2", + "react-native-permissions": "^5.4.0", "react-native-safe-area-context": "^4.9.0", - "react-native-screens": "^3.29.0", + "react-native-screens": "^4.0.0", "react-native-snackbar": "^2.8.0" }, "devDependencies": {