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
9 changes: 9 additions & 0 deletions sdk/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,14 @@
android:noHistory="true"
android:theme="@style/Theme.MindboxTransparent">
</activity>
<activity
android:name="cloud.mindbox.mobile_sdk.inapp.presentation.actions.RuntimePermissionRequestActivity"
android:excludeFromRecents="true"
android:exported="false"
android:finishOnTaskLaunch="true"
android:launchMode="singleTask"
android:noHistory="true"
android:theme="@style/Theme.MindboxTransparent">
</activity>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package cloud.mindbox.mobile_sdk.inapp.presentation.actions

import android.app.Activity
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.ViewGroup
import cloud.mindbox.mobile_sdk.logger.mindboxLogW

internal class RuntimePermissionRequestActivity : Activity() {

companion object {
private const val REQUEST_CODE: Int = 125130
internal const val EXTRA_REQUEST_ID: String = "runtime_permission_request_id"
internal const val EXTRA_PERMISSIONS: String = "runtime_permission_permissions"
}

private var requestId: String? = null
private var isResultSent: Boolean = false

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
window.decorView.setBackgroundColor(Color.TRANSPARENT)
window.decorView.isClickable = false
window.setDimAmount(0f)
val actualRequestId: String = intent?.getStringExtra(EXTRA_REQUEST_ID).orEmpty()
val permissions: Array<String> = intent?.getStringArrayExtra(EXTRA_PERMISSIONS)
?.map { permission: String -> permission }
?.toTypedArray()
?: emptyArray()
if (actualRequestId.isBlank() || permissions.isEmpty()) {
finish()
return
}
requestId = actualRequestId
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(permissions, REQUEST_CODE)
} else {
RuntimePermissionRequestBridge.resolve(actualRequestId, false)
isResultSent = true
finish()
}
}

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode != REQUEST_CODE) {
return
}
val isGranted: Boolean = grantResults.isNotEmpty() && grantResults.all { result: Int ->
result == android.content.pm.PackageManager.PERMISSION_GRANTED
}
val actualRequestId: String = requestId.orEmpty()
if (actualRequestId.isNotBlank()) {
RuntimePermissionRequestBridge.resolve(actualRequestId, isGranted)
isResultSent = true
}
finish()
}

override fun onDestroy() {
if (!isResultSent && isFinishing && !isChangingConfigurations) {
val actualRequestId: String = requestId.orEmpty()
if (actualRequestId.isNotBlank()) {
mindboxLogW("Permission request activity closed before result for id=$actualRequestId")
RuntimePermissionRequestBridge.resolve(actualRequestId, false)
}
}
super.onDestroy()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cloud.mindbox.mobile_sdk.inapp.presentation.actions

import kotlinx.coroutines.CompletableDeferred
import java.util.concurrent.ConcurrentHashMap

internal object RuntimePermissionRequestBridge {

private val pendingRequestsById: MutableMap<String, CompletableDeferred<Boolean>> = ConcurrentHashMap()

fun register(requestId: String): CompletableDeferred<Boolean> {
val deferred: CompletableDeferred<Boolean> = CompletableDeferred()
pendingRequestsById[requestId] = deferred
return deferred
}

fun resolve(requestId: String, isGranted: Boolean) {
val deferred: CompletableDeferred<Boolean> = pendingRequestsById.remove(requestId) ?: return
if (!deferred.isCompleted) {
deferred.complete(isGranted)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ public enum class WebViewAction {

@SerializedName("haptic")
HAPTIC,

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

@InternalMindboxApi
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cloud.mindbox.mobile_sdk.inapp.presentation.view

import android.app.Activity
import android.app.Application
import android.net.Uri
import android.view.ViewGroup
Expand All @@ -8,11 +9,9 @@ import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AlertDialog
import androidx.core.net.toUri
import cloud.mindbox.mobile_sdk.BuildConfig
import cloud.mindbox.mobile_sdk.Mindbox
import cloud.mindbox.mobile_sdk.*
import cloud.mindbox.mobile_sdk.annotations.InternalMindboxApi
import cloud.mindbox.mobile_sdk.di.mindboxInject
import cloud.mindbox.mobile_sdk.fromJson
import cloud.mindbox.mobile_sdk.inapp.data.dto.BackgroundDto
import cloud.mindbox.mobile_sdk.inapp.data.managers.SessionStorageManager
import cloud.mindbox.mobile_sdk.inapp.data.validators.BridgeMessageValidator
Expand All @@ -35,7 +34,6 @@ import cloud.mindbox.mobile_sdk.managers.GatewayManager
import cloud.mindbox.mobile_sdk.models.Configuration
import cloud.mindbox.mobile_sdk.models.getShortUserAgent
import cloud.mindbox.mobile_sdk.models.operation.request.FailureReason
import cloud.mindbox.mobile_sdk.safeAs
import cloud.mindbox.mobile_sdk.utils.MindboxUtils.Stopwatch
import com.google.gson.Gson
import kotlinx.coroutines.CancellationException
Expand Down Expand Up @@ -93,6 +91,13 @@ internal class WebViewInAppViewHolder(
HapticFeedbackExecutorImpl(appContext)
}

private val webViewPermissionRequester: WebViewPermissionRequester by lazy {
WebViewPermissionRequesterImpl(
context = appContext,
permissionManager = permissionManager
)
}

override fun bind() {}

suspend fun sendActionAndAwaitResponse(
Expand Down Expand Up @@ -143,6 +148,7 @@ internal class WebViewInAppViewHolder(
registerSuspend(WebViewAction.LOCAL_STATE_GET, ::handleLocalStateGetAction)
registerSuspend(WebViewAction.LOCAL_STATE_SET, ::handleLocalStateSetAction)
registerSuspend(WebViewAction.LOCAL_STATE_INIT, ::handleLocalStateInitAction)
registerSuspend(WebViewAction.PERMISSION_REQUEST, ::handlePermissionAction)
register(WebViewAction.READY) {
handleReadyAction(
configuration = configuration,
Expand Down Expand Up @@ -283,6 +289,22 @@ internal class WebViewInAppViewHolder(
return localStateStore.initState(payload)
}

private suspend fun handlePermissionAction(message: BridgeMessage.Request): String {
val payload: String = message.payload ?: BridgeMessage.EMPTY_PAYLOAD
val typeString: String? = JSONObject(payload).getString(PERMISSION_PAYLOAD_TYPE_FIELD_NAME)
val type: PermissionType? = runCatching { typeString.enumValue<PermissionType>() }.getOrNull()
requireNotNull(type) { "Unknown permission type: $typeString" }

val activity: Activity? = webViewController?.view?.context?.safeAs<Activity>()
checkNotNull(activity) { "Not found activity for permission request" }

val permissionRequestResult: PermissionActionResponse = webViewPermissionRequester.requestPermission(
activity,
type
)
return gson.toJson(permissionRequestResult)
}

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
Loading
Loading