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
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ internal interface MindboxNotificationManager {

fun isNotificationEnabled(): Boolean

fun openNotificationSettings(activity: Activity)
fun openNotificationSettings(activity: Activity, channelId: String? = null)

fun openApplicationSettings(activity: Activity)

fun requestPermission(activity: Activity)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cloud.mindbox.mobile_sdk.inapp.presentation
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings
import androidx.core.app.NotificationManagerCompat
Expand All @@ -11,7 +12,7 @@ import cloud.mindbox.mobile_sdk.logger.mindboxLogE
import cloud.mindbox.mobile_sdk.logger.mindboxLogI
import cloud.mindbox.mobile_sdk.managers.RequestPermissionManager
import cloud.mindbox.mobile_sdk.utils.Constants
import cloud.mindbox.mobile_sdk.utils.LoggingExceptionHandler
import cloud.mindbox.mobile_sdk.utils.loggingRunCatching

internal class MindboxNotificationManagerImpl(
private val context: Context,
Expand All @@ -29,32 +30,41 @@ internal class MindboxNotificationManagerImpl(
}
}

override fun openNotificationSettings(activity: Activity) {
LoggingExceptionHandler.runCatching {
override fun openNotificationSettings(activity: Activity, channelId: String?) {
loggingRunCatching {
val intent = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, activity.packageName)
putExtra(Settings.EXTRA_CHANNEL_ID, channelId)
}
}

else -> {
Intent(Constants.NOTIFICATION_SETTINGS).apply {
putExtra(Constants.APP_PACKAGE_NAME, activity.packageName)
putExtra(Constants.APP_UID_NAME, activity.applicationInfo.uid)
}
}
}
mindboxLogI("Opening notification settings.")
mindboxLogI("Opening notification settings")
activity.startActivity(intent)
}
}

override fun openApplicationSettings(activity: Activity) {
loggingRunCatching {
val packageUri: Uri = Uri.fromParts("package", activity.packageName, null)
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageUri)
mindboxLogI("Opening application settings")
activity.startActivity(intent)
}
}

override fun requestPermission(activity: Activity) {
LoggingExceptionHandler.runCatching {
loggingRunCatching {
if (NotificationManagerCompat.from(context).areNotificationsEnabled()) {
mindboxLogI("Notification is enabled now, don't try request permission")
return@runCatching
return@loggingRunCatching
Comment on lines 63 to +67
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In requestPermission(), the else branch later in this method calls openNotificationSettings(activity) without the new channelId argument. Since the override openNotificationSettings(activity: Activity, channelId: String?) doesn’t declare a default value, this will not compile; pass null explicitly (or add a 1-arg overload / default on the override).

Copilot uses AI. Check for mistakes.
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ public enum class WebViewAction {

@SerializedName(value = "permission.request")
PERMISSION_REQUEST,

@SerializedName(value = "settings.open")
SETTINGS_OPEN,
Comment on lines 60 to +65
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new serialized action ("settings.open") is added here, but there’s no unit test asserting Gson maps this string to WebViewAction.SETTINGS_OPEN. Please extend the existing WebView bridge serialization tests (e.g., the one that checks "permission.request") to cover this new action mapping.

Copilot uses AI. Check for mistakes.
}

@InternalMindboxApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import cloud.mindbox.mobile_sdk.inapp.domain.models.InAppType
import cloud.mindbox.mobile_sdk.inapp.domain.models.InAppTypeWrapper
import cloud.mindbox.mobile_sdk.inapp.domain.models.Layer
import cloud.mindbox.mobile_sdk.inapp.presentation.InAppCallback
import cloud.mindbox.mobile_sdk.inapp.presentation.MindboxNotificationManager
import cloud.mindbox.mobile_sdk.inapp.presentation.MindboxView
import cloud.mindbox.mobile_sdk.inapp.webview.*
import cloud.mindbox.mobile_sdk.logger.mindboxLogD
Expand All @@ -36,6 +37,7 @@ import cloud.mindbox.mobile_sdk.models.getShortUserAgent
import cloud.mindbox.mobile_sdk.models.operation.request.FailureReason
import cloud.mindbox.mobile_sdk.utils.MindboxUtils.Stopwatch
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.cancel
Expand Down Expand Up @@ -77,6 +79,7 @@ internal class WebViewInAppViewHolder(
private val gatewayManager: GatewayManager by mindboxInject { gatewayManager }
private val sessionStorageManager: SessionStorageManager by mindboxInject { sessionStorageManager }
private val permissionManager: PermissionManager by mindboxInject { permissionManager }
private val mindboxNotificationManager: MindboxNotificationManager by mindboxInject { mindboxNotificationManager }
private val appContext: Application by mindboxInject { appContext }
private val operationExecutor: WebViewOperationExecutor by lazy {
MindboxWebViewOperationExecutor()
Expand Down Expand Up @@ -149,6 +152,7 @@ internal class WebViewInAppViewHolder(
registerSuspend(WebViewAction.LOCAL_STATE_SET, ::handleLocalStateSetAction)
registerSuspend(WebViewAction.LOCAL_STATE_INIT, ::handleLocalStateInitAction)
registerSuspend(WebViewAction.PERMISSION_REQUEST, ::handlePermissionAction)
register(WebViewAction.SETTINGS_OPEN, ::handleSettingsOpenAction)
register(WebViewAction.READY) {
handleReadyAction(
configuration = configuration,
Expand Down Expand Up @@ -305,6 +309,22 @@ internal class WebViewInAppViewHolder(
return gson.toJson(permissionRequestResult)
}

private fun handleSettingsOpenAction(message: BridgeMessage.Request): String {
val payload: String = message.payload ?: BridgeMessage.EMPTY_PAYLOAD
val settingsOpenRequest: SettingsOpenRequest? = gson.fromJson<SettingsOpenRequest>(payload).getOrNull()
requireNotNull(settingsOpenRequest)

val targetType = settingsOpenRequest.target.enumValue<SettingsOpenTargetType>()
Comment on lines +315 to +317
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requireNotNull(settingsOpenRequest) will produce a very generic error in the JS bridge response ("Required value was null") when payload parsing fails. Consider providing a descriptive message (e.g., invalid/missing target) and parsing target defensively (similar to handlePermissionAction, using runCatching { ...enumValue... }.getOrNull() + requireNotNull(...) { "Unknown settings target: ..." }) so the WebView receives actionable error details.

Suggested change
requireNotNull(settingsOpenRequest)
val targetType = settingsOpenRequest.target.enumValue<SettingsOpenTargetType>()
requireNotNull(settingsOpenRequest) { "Invalid or missing settings open request payload" }
val targetString: String? = settingsOpenRequest.target
val targetType: SettingsOpenTargetType? =
runCatching { targetString?.enumValue<SettingsOpenTargetType>() }.getOrNull()
requireNotNull(targetType) { "Unknown settings target: $targetString" }

Copilot uses AI. Check for mistakes.
val activity: Activity? = webViewController?.view?.context?.safeAs<Activity>()
checkNotNull(activity) { "Not found activity for open settings" }

when (targetType) {
SettingsOpenTargetType.NOTIFICATIONS -> mindboxNotificationManager.openNotificationSettings(activity, settingsOpenRequest.channelId)
SettingsOpenTargetType.APPLICATION -> mindboxNotificationManager.openApplicationSettings(activity)
}
return BridgeMessage.SUCCESS_PAYLOAD
}

private fun createWebViewController(layer: Layer.WebViewLayer): WebViewController {
mindboxLogI("Creating WebView for In-App: ${wrapper.inAppType.inAppId} with layer ${layer.type}")
val controller: WebViewController = WebViewController.create(currentDialog.context, BuildConfig.DEBUG)
Expand Down Expand Up @@ -687,4 +707,16 @@ internal class WebViewInAppViewHolder(
private data class ErrorPayload(
val error: String
)

private data class SettingsOpenRequest(
@SerializedName("target")
val target: String,
@SerializedName("channelId")
val channelId: String?
)

private enum class SettingsOpenTargetType {
NOTIFICATIONS,
APPLICATION
}
}
Loading