Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## [Unreleased]

### Changes
- Upgrade Android SDK dependency to v2.14.2
- Upgrade iOS SDK dependency to v2.14.3


## [2.14.1] - 2025-09-12

### Changes
Expand Down
4 changes: 2 additions & 2 deletions MindboxSdk.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ Pod::Spec.new do |s|

s.dependency "React-Core"

s.dependency "Mindbox", "2.14.1"
s.dependency "MindboxNotifications", "2.14.1"
s.dependency "Mindbox", "2.14.3"
s.dependency "MindboxNotifications", "2.14.3"
end
3 changes: 2 additions & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ android {
targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
versionCode 1
versionName "1.0"
consumerProguardFiles 'consumer-rules.pro'
}

buildTypes {
Expand Down Expand Up @@ -126,5 +127,5 @@ dependencies {
// noinspection GradleDynamicVersion
api 'com.facebook.react:react-native:+'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
api 'cloud.mindbox:mobile-sdk:2.14.1'
api 'cloud.mindbox:mobile-sdk:2.14.2'
}
5 changes: 5 additions & 0 deletions android/consumer-rules.pro
Original file line number Diff line number Diff line change
@@ -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();
}
7 changes: 6 additions & 1 deletion android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mindboxsdk">

<application>
<provider
android:name="com.mindboxsdk.MindboxSdkInitProvider"
android:authorities="${applicationId}.mindbox.init"
android:exported="false" />
</application>
</manifest>
55 changes: 55 additions & 0 deletions android/src/main/java/com/mindboxsdk/MindboxEventEmitter.kt
Original file line number Diff line number Diff line change
@@ -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: MindboxSdkLifecycleEvent) {
when (event) {
is MindboxSdkLifecycleEvent.NewIntent -> handleNewIntent(event.reactContext, event.intent)
is MindboxSdkLifecycleEvent.ActivityCreated -> handleActivityCreated(event.reactContext, event.activity)
is MindboxSdkLifecycleEvent.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
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.mindboxsdk

internal interface MindboxEventSubscriber {
fun onEvent(event: MindboxSdkLifecycleEvent)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.mindboxsdk

import android.os.Bundle
import android.util.Log
import cloud.mindbox.mobile_sdk.pushes.MindboxPushService

internal class MindboxPushServicesDiscovery(private val meta: Bundle?) {

companion object {
private const val TAG = "MindboxPushServicesDiscovery"
private const val PUSH_PROVIDER_PREFIX = "cloud.mindbox.push.provider."
}

private val providerPriority: Map<String, Int> by lazy(LazyThreadSafetyMode.NONE) {
mapOf("FCM" to 0, "HCM" to 1, "RuStore" to 2)
}

val services: List<MindboxPushService> by lazy(LazyThreadSafetyMode.NONE) {
if (meta == null) {
return@lazy emptyList()
}
meta.keySet()
.asSequence()
.filter { it.startsWith(PUSH_PROVIDER_PREFIX) }
.mapNotNull { key -> meta.getString(key)?.takeIf { it.isNotBlank() } }
.mapNotNull { className -> loadPushService(className) }
.distinctBy { it::class.java.name }
.sortedWith(
compareBy<MindboxPushService> { providerPriority[it.tag] ?: Int.MAX_VALUE }
.thenBy { it.tag }
)
.toList()
.also { list ->
Log.i(TAG, "Found ${list.size} push services: ${list.joinToString { it.tag }}")
list
}
}

private fun loadPushService(className: String): MindboxPushService? {
return runCatching {
val clazz = Class.forName(className)

if (!MindboxPushService::class.java.isAssignableFrom(clazz)) {
Log.w(TAG, "Class $className does not implement MindboxPushService")
return@runCatching null
}
getPushServiceInstance(clazz)?.also {
Log.i(TAG, "Loaded push provider: $className")
} ?: run {
Log.e(TAG, "Failed to create instance for provider: $className")
null
}
}.getOrElse { exception ->
when (exception) {
is ClassNotFoundException -> Log.e(TAG, "Push provider class not found: $className", exception)
else -> Log.e(TAG, "Failed to load push provider: $className", exception)
}
null
}
}

private fun getPushServiceInstance(clazz: Class<*>): MindboxPushService? {
return runCatching {
val instanceField = clazz.getDeclaredField("INSTANCE")
instanceField.isAccessible = true
instanceField.get(null) as? MindboxPushService
}.getOrElse {
Log.w(TAG, "Failed to get instance for ${clazz.name}")
null
}
}
}
66 changes: 66 additions & 0 deletions android/src/main/java/com/mindboxsdk/MindboxSdkInitProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
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.os.Bundle
import android.util.Log
import cloud.mindbox.mobile_sdk.pushes.MindboxPushService
import cloud.mindbox.mobile_sdk.Mindbox

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(TAG, "onCreate initProvider.")
(context?.applicationContext as? Application)?.let { application ->
val meta = application.readMetaData()
if (isAutoInitEnabled(application, meta)) {
Log.i(TAG, "Automatic initialization is enabled.")
Mindbox.initPushServices(application, MindboxPushServicesDiscovery(meta).services)
MindboxSdkLifecycleListener.register(application)
} else {
Log.i(TAG, "Automatic initialization is disabled.")
}
}
}.onFailure { error ->
Log.e(TAG, "Automatic initialization failed", error)
}
return true
}

private fun isAutoInitEnabled(application: Application, metaData: Bundle?): Boolean =
metaData
?.getBoolean(AUTO_INIT_ENABLED_KEY, false)
?: false

private fun Application.readMetaData(): Bundle? = runCatching {
packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA).metaData
}.getOrNull()

override fun query(
uri: Uri,
projection: Array<String>?,
selection: String?,
selectionArgs: Array<String>?,
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<String>?): Int = 0
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<String>?
): Int = 0
}
11 changes: 11 additions & 0 deletions android/src/main/java/com/mindboxsdk/MindboxSdkLifecycleEvent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.mindboxsdk

import android.app.Activity
import android.content.Intent
import com.facebook.react.bridge.ReactContext

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()
}
Loading
Loading