diff --git a/PrivacySandboxKotlin/client-app/src/main/java/com/example/client/MainActivity.kt b/PrivacySandboxKotlin/client-app/src/main/java/com/example/client/MainActivity.kt index 5375fff..022a8ac 100644 --- a/PrivacySandboxKotlin/client-app/src/main/java/com/example/client/MainActivity.kt +++ b/PrivacySandboxKotlin/client-app/src/main/java/com/example/client/MainActivity.kt @@ -134,17 +134,13 @@ class MainActivity : AppCompatActivity() { // Mediated Banner Ad is shown when RUNTIME_MEDIATEE Mediation option is chosen. val mediationType = MediationOption.entries[mediationDropDownMenu.selectedItemId.toInt()].toString() - if (mediationType == MediationOption.INAPP_MEDIATEE.toString()) { - makeToast("RE_SDK<>InApp Mediated Banner Ad not yet implemented!") - } else { - bannerAd.loadAd( - this@MainActivity, - PACKAGE_NAME, - shouldStartActivityPredicate(), - loadWebView, - mediationType - ) - } + bannerAd.loadAd( + this@MainActivity, + PACKAGE_NAME, + shouldStartActivityPredicate(), + loadWebView, + mediationType + ) } private fun showFullscreenView() = lifecycleScope.launch { diff --git a/PrivacySandboxKotlin/example-sdk/src/main/java/com/example/api/SdkService.kt b/PrivacySandboxKotlin/example-sdk/src/main/java/com/example/api/SdkService.kt index 6cd9f34..e856d13 100644 --- a/PrivacySandboxKotlin/example-sdk/src/main/java/com/example/api/SdkService.kt +++ b/PrivacySandboxKotlin/example-sdk/src/main/java/com/example/api/SdkService.kt @@ -15,15 +15,23 @@ */ package com.example.api +import android.os.Bundle import androidx.privacysandbox.tools.PrivacySandboxService @PrivacySandboxService interface SdkService { + + /** + * App has to call this API after loadSdk("mediator") call. + * Mediatee and Adapter SDKs are loaded by the Mediator when this API is called. + */ + suspend fun initialise() + suspend fun getMessage(): String suspend fun createFile(sizeInMb: Int): String - suspend fun getBanner(request: SdkBannerRequest, mediationType: String): SdkSandboxedUiAdapter? + suspend fun getBanner(request: SdkBannerRequest, mediationType: String): Bundle? suspend fun getFullscreenAd(mediationType: String): FullscreenAd diff --git a/PrivacySandboxKotlin/example-sdk/src/main/java/com/example/implementation/SdkProvider.kt b/PrivacySandboxKotlin/example-sdk/src/main/java/com/example/implementation/SdkProvider.kt index acfa419..0cdee97 100644 --- a/PrivacySandboxKotlin/example-sdk/src/main/java/com/example/implementation/SdkProvider.kt +++ b/PrivacySandboxKotlin/example-sdk/src/main/java/com/example/implementation/SdkProvider.kt @@ -16,22 +16,11 @@ package com.example.implementation import android.content.Context -import android.os.Bundle -import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat -import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat import com.example.api.AbstractSandboxedSdkProviderCompat import com.example.api.SdkService -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch /** Provides an [SdkService] implementation when the SDK is loaded. */ class SdkProvider : AbstractSandboxedSdkProviderCompat() { - - private val adapterSdkName = "com.mediateeadapter.sdk" - - private val coroutineScope = CoroutineScope(Dispatchers.Main) - /** * Returns the [SdkService] implementation. Called when the SDK is loaded. * @@ -39,24 +28,4 @@ class SdkProvider : AbstractSandboxedSdkProviderCompat() { * the Privacy Sandbox API Compiler plugin as the entry point for the app/SDK communication. */ override fun createSdkService(context: Context): SdkService = SdkServiceImpl(context) - - /** - * Does the work needed for the SDK to start handling requests. SDK should do any work to be - * ready to handle upcoming requests. - * - * This function is called by the SDK sandbox after it loads the SDK. - * - * Mediator initialises the Runtime-enabled adapters in its own initialisation call. - */ - override fun onLoadSdk(params: Bundle): SandboxedSdkCompat { - coroutineScope.launch { - initialiseAdapters() - } - return super.onLoadSdk(params) - } - - private suspend fun initialiseAdapters() { - SdkSandboxControllerCompat.from(checkNotNull(context) { "Cannot initialise adapters!" }) - .loadSdk(adapterSdkName, Bundle.EMPTY) - } } diff --git a/PrivacySandboxKotlin/example-sdk/src/main/java/com/example/implementation/SdkServiceImpl.kt b/PrivacySandboxKotlin/example-sdk/src/main/java/com/example/implementation/SdkServiceImpl.kt index d84e730..c5423d2 100644 --- a/PrivacySandboxKotlin/example-sdk/src/main/java/com/example/implementation/SdkServiceImpl.kt +++ b/PrivacySandboxKotlin/example-sdk/src/main/java/com/example/implementation/SdkServiceImpl.kt @@ -19,6 +19,7 @@ import android.content.Context import android.os.Bundle import android.os.RemoteException import android.util.Log +import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory import com.example.R import com.example.api.FullscreenAd @@ -33,17 +34,27 @@ import androidx.privacysandbox.ui.core.SandboxedSdkViewUiInfo import androidx.privacysandbox.ui.core.SessionObserver import androidx.privacysandbox.ui.core.SessionObserverContext import androidx.privacysandbox.ui.core.SessionObserverFactory +import androidx.privacysandbox.ui.provider.toCoreLibInfo import com.example.api.MediateeAdapterInterface -import com.example.api.SdkSandboxedUiAdapter class SdkServiceImpl(private val context: Context) : SdkService { - override suspend fun getMessage(): String = "Hello from Privacy Sandbox!" private var inAppMediateeAdapter: MediateeAdapterInterface? = null private var mediateeAdapter: MediateeAdapterInterface? = null - + + private val adapterSdkName = "com.mediateeadapter.sdk" + private val mediateeSdkName = "com.mediatee.sdk" private val tag = "ExampleSdk" + override suspend fun initialise() { + val sandboxController = SdkSandboxControllerCompat.from(context) + sandboxController.loadSdk(mediateeSdkName, Bundle.EMPTY) + // Adapter should only be loaded after Mediatee is loaded. + sandboxController.loadSdk(adapterSdkName, Bundle.EMPTY) + } + + override suspend fun getMessage(): String = "Hello from Privacy Sandbox!" + override suspend fun createFile(sizeInMb: Int): String { val path = Paths.get( context.applicationContext.dataDir.path, "file.txt" @@ -63,11 +74,21 @@ class SdkServiceImpl(private val context: Context) : SdkService { override suspend fun getBanner( request: SdkBannerRequest, mediationType: String - ): SdkSandboxedUiAdapter? { + ): Bundle? { if (mediationType == context.getString(R.string.mediation_option_none)) { val bannerAdAdapter = SdkSandboxedUiAdapterImpl(context, request, null) bannerAdAdapter.addObserverFactory(SessionObserverFactoryImpl()) - return bannerAdAdapter + return bannerAdAdapter.toCoreLibInfo(context) + } + // For In-app mediatee, SandboxedUiAdapter returned by mediatee is not wrapped, it is + // directly returned to app. This is to avoid nested remote rendering. + // There is no overlay in this case for this reason. + if (mediationType == context.getString(R.string.mediation_option_inapp_mediatee)) { + return inAppMediateeAdapter?.getBannerAd( + request.appPackageName, + request.activityLauncher, + request.isWebViewBannerAd + ) } return SdkSandboxedUiAdapterImpl( context, @@ -79,7 +100,7 @@ class SdkServiceImpl(private val context: Context) : SdkService { request.isWebViewBannerAd ) ) { "No banner Ad received from mediatee!" }) - ) + ).toCoreLibInfo(context) } override suspend fun getFullscreenAd(mediationType: String): FullscreenAd { diff --git a/PrivacySandboxKotlin/existing-sdk/src/main/java/com/existing/sdk/BannerAd.kt b/PrivacySandboxKotlin/existing-sdk/src/main/java/com/existing/sdk/BannerAd.kt index 3e735c9..a316cc0 100644 --- a/PrivacySandboxKotlin/existing-sdk/src/main/java/com/existing/sdk/BannerAd.kt +++ b/PrivacySandboxKotlin/existing-sdk/src/main/java/com/existing/sdk/BannerAd.kt @@ -22,6 +22,7 @@ import android.widget.LinearLayout import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.privacysandbox.activity.client.createSdkActivityLauncher +import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory import androidx.privacysandbox.ui.client.view.SandboxedSdkView import androidx.privacysandbox.ui.core.SandboxedUiAdapter import com.example.api.SdkBannerRequest @@ -69,7 +70,12 @@ class BannerAd(context: Context, attrs: AttributeSet) : LinearLayout(context, at val launcher = baseActivity.createSdkActivityLauncher(allowSdkActivityLaunch) val request = SdkBannerRequest(message, launcher, shouldLoadWebView) - return ExistingSdk.loadSdkIfNeeded(context)?.getBanner(request, mediationType) + return SandboxedUiAdapterFactory.createFromCoreLibInfo( + checkNotNull( + ExistingSdk.loadSdkIfNeeded( + context + )?.getBanner(request, mediationType) + ) { "No banner Ad received from ad SDK!" }) } private fun addViewToLayout(view: View) { diff --git a/PrivacySandboxKotlin/existing-sdk/src/main/java/com/existing/sdk/ExistingSdk.kt b/PrivacySandboxKotlin/existing-sdk/src/main/java/com/existing/sdk/ExistingSdk.kt index b235285..bbad782 100644 --- a/PrivacySandboxKotlin/existing-sdk/src/main/java/com/existing/sdk/ExistingSdk.kt +++ b/PrivacySandboxKotlin/existing-sdk/src/main/java/com/existing/sdk/ExistingSdk.kt @@ -80,6 +80,8 @@ class ExistingSdk(private val context: Context) { val sandboxedSdk = sandboxManagerCompat.loadSdk(SDK_NAME, Bundle.EMPTY) remoteInstance = SdkServiceFactory.wrapToSdkService(sandboxedSdk.getInterface()!!) + // Initialise Adapters and Mediatees. + remoteInstance?.initialise() return remoteInstance } catch (e: LoadSdkCompatException) { Log.e(TAG, "Failed to load SDK, error code: ${e.loadSdkErrorCode}", e) diff --git a/PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/README.md b/PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/README.md new file mode 100644 index 0000000..e251b5d --- /dev/null +++ b/PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/README.md @@ -0,0 +1,9 @@ +# In App Mediatee Adapter SDK + +This SDK is Runtime Aware but runs in the App process. It facilitates interaction between mediator +and in-app mediatee. + +Implements MediateeAdapterInterface declared by mediator (example-sdk). + +This could be owned by the mediator sdk during transition, or optionally all the logic here could +also be a part of RA_SDK (existing-sdk). diff --git a/PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/build.gradle b/PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/build.gradle index 5fe51f8..14a4207 100644 --- a/PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/build.gradle +++ b/PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/build.gradle @@ -49,13 +49,16 @@ dependencies { debugImplementation project(':example-sdk-bundle') implementation project(':inapp-mediatee-sdk') - implementation 'androidx.privacysandbox.activity:activity-core:1.0.0-alpha01' - implementation 'androidx.privacysandbox.activity:activity-provider:1.0.0-alpha01' implementation 'androidx.activity:activity-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.7.0' - implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.10' implementation "androidx.lifecycle:lifecycle-common:2.7.0" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1" + + implementation "androidx.privacysandbox.ui:ui-core:$privacy_sandbox_ui_version" + implementation "androidx.privacysandbox.ui:ui-provider:$privacy_sandbox_ui_version" + + implementation "androidx.privacysandbox.activity:activity-core:$privacy_sandbox_activity_version" + implementation "androidx.privacysandbox.activity:activity-provider:$privacy_sandbox_activity_version" } diff --git a/PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/src/main/java/java/com/inappmediateeadapter/implementation/InAppMediateeSdkAdapter.kt b/PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/src/main/java/com/inappmediateeadapter/implementation/InAppMediateeSdkAdapter.kt similarity index 87% rename from PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/src/main/java/java/com/inappmediateeadapter/implementation/InAppMediateeSdkAdapter.kt rename to PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/src/main/java/com/inappmediateeadapter/implementation/InAppMediateeSdkAdapter.kt index 0ad3b10..52f521b 100644 --- a/PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/src/main/java/java/com/inappmediateeadapter/implementation/InAppMediateeSdkAdapter.kt +++ b/PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/src/main/java/com/inappmediateeadapter/implementation/InAppMediateeSdkAdapter.kt @@ -2,6 +2,7 @@ package com.inappmediateeadapter.implementation import android.content.Context import android.os.Bundle +import androidx.privacysandbox.ui.provider.toCoreLibInfo import androidx.privacysandbox.activity.core.SdkActivityLauncher import com.inappmediatee.sdk.InAppMediateeSdk import com.example.api.MediateeAdapterInterface @@ -20,7 +21,8 @@ class InAppMediateeSdkAdapter(private val context: Context): MediateeAdapterInte activityLauncher: SdkActivityLauncher, isWebViewBannerAd: Boolean ): Bundle { - TODO("Not yet implemented") + return SandboxedUiAdapterImpl(inAppMediateeSdk.loadBannerAd(isWebViewBannerAd)) + .toCoreLibInfo(context) } override suspend fun loadFullscreenAd() { diff --git a/PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/src/main/java/com/inappmediateeadapter/implementation/SandboxedUiAdapterImpl.kt b/PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/src/main/java/com/inappmediateeadapter/implementation/SandboxedUiAdapterImpl.kt new file mode 100644 index 0000000..14e10cc --- /dev/null +++ b/PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/src/main/java/com/inappmediateeadapter/implementation/SandboxedUiAdapterImpl.kt @@ -0,0 +1,74 @@ +package com.inappmediateeadapter.implementation + +import android.content.Context +import android.content.res.Configuration +import android.os.Bundle +import android.os.IBinder +import android.view.View +import androidx.privacysandbox.ui.core.SandboxedUiAdapter +import androidx.privacysandbox.ui.core.SessionObserverFactory +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.cancel +import java.util.concurrent.Executor + +class SandboxedUiAdapterImpl(private val mediateeAdView: View): SandboxedUiAdapter { + override fun openSession( + context: Context, + windowInputToken: IBinder, + initialWidth: Int, + initialHeight: Int, + isZOrderOnTop: Boolean, + clientExecutor: Executor, + client: SandboxedUiAdapter.SessionClient + ) { + val session = SdkUiSession(clientExecutor, mediateeAdView) + clientExecutor.execute { + client.onSessionOpened(session) + } + } + + override fun addObserverFactory(sessionObserverFactory: SessionObserverFactory) { + // Adds a [SessionObserverFactory] with a [SandboxedUiAdapter] for tracking UI presentation + // state across UI sessions. This has no effect on already open sessions. + } + + override fun removeObserverFactory(sessionObserverFactory: SessionObserverFactory) { + // Removes a [SessionObserverFactory] from a [SandboxedUiAdapter], if it has been + // previously added with [addObserverFactory]. + } +} + +private class SdkUiSession(clientExecutor: Executor, mediateeAdView: View) : + SandboxedUiAdapter.Session { + + /** A scope for launching coroutines in the client executor. */ + private val scope = CoroutineScope(clientExecutor.asCoroutineDispatcher() + Job()) + + override val signalOptions: Set = setOf() + + override val view: View = mediateeAdView + + override fun close() { + // Notifies that the client has closed the session. It's a good opportunity to dispose + // any resources that were acquired to maintain the session. + scope.cancel() + } + + override fun notifyConfigurationChanged(configuration: Configuration) { + // Notifies that the device configuration has changed and affected the app. + } + + override fun notifyResized(width: Int, height: Int) { + // Notifies that the size of the presentation area in the app has changed. + } + + override fun notifyUiChanged(uiContainerInfo: Bundle) { + // Notify the session when the presentation state of its UI container has changed. + } + + override fun notifyZOrderChanged(isZOrderOnTop: Boolean) { + // Notifies that the Z order has changed for the UI associated by this session. + } +} \ No newline at end of file diff --git a/PrivacySandboxKotlin/inapp-mediatee-sdk/src/main/java/com/inappmediatee/sdk/InAppMediateeSdk.kt b/PrivacySandboxKotlin/inapp-mediatee-sdk/src/main/java/com/inappmediatee/sdk/InAppMediateeSdk.kt index 0464c77..55df26d 100644 --- a/PrivacySandboxKotlin/inapp-mediatee-sdk/src/main/java/com/inappmediatee/sdk/InAppMediateeSdk.kt +++ b/PrivacySandboxKotlin/inapp-mediatee-sdk/src/main/java/com/inappmediatee/sdk/InAppMediateeSdk.kt @@ -2,11 +2,25 @@ package com.inappmediatee.sdk import android.content.Context import android.content.Intent +import android.view.View +import android.webkit.WebView +import com.inappmediatee.R class InAppMediateeSdk(private val context: Context) { + private val webViewUrl = "https://www.google.com" + + fun loadBannerAd(isWebViewBannerAd: Boolean) : View { + if (isWebViewBannerAd) { + val webview = WebView(context) + webview.loadUrl(webViewUrl) + return webview + } + return View.inflate(context, R.layout.banner, null) + } + fun loadFullscreenAd() { - // All the heavy logic to load fullscreen Ad that Mdiatee needs to perform goes here. + // All the heavy logic to load fullscreen Ad that Mediatee needs to perform goes here. } fun showFullscreenAd() { diff --git a/PrivacySandboxKotlin/inapp-mediatee-sdk/src/main/res/layout/banner.xml b/PrivacySandboxKotlin/inapp-mediatee-sdk/src/main/res/layout/banner.xml new file mode 100644 index 0000000..ee662a5 --- /dev/null +++ b/PrivacySandboxKotlin/inapp-mediatee-sdk/src/main/res/layout/banner.xml @@ -0,0 +1,38 @@ + + + + + + + + + diff --git a/PrivacySandboxKotlin/inapp-mediatee-sdk/src/main/res/values/strings.xml b/PrivacySandboxKotlin/inapp-mediatee-sdk/src/main/res/values/strings.xml index 6fa7ad7..8005e27 100644 --- a/PrivacySandboxKotlin/inapp-mediatee-sdk/src/main/res/values/strings.xml +++ b/PrivacySandboxKotlin/inapp-mediatee-sdk/src/main/res/values/strings.xml @@ -13,4 +13,5 @@ --> Full screen ad launched by in-app mediatee! + Ad from In-app Mediatee SDK (no overlay from mediator) diff --git a/PrivacySandboxKotlin/mediatee-sdk-adapter/src/main/java/com/mediateeadapter/implementation/SdkProvider.kt b/PrivacySandboxKotlin/mediatee-sdk-adapter/src/main/java/com/mediateeadapter/implementation/SdkProvider.kt index 1045f9e..22ff278 100644 --- a/PrivacySandboxKotlin/mediatee-sdk-adapter/src/main/java/com/mediateeadapter/implementation/SdkProvider.kt +++ b/PrivacySandboxKotlin/mediatee-sdk-adapter/src/main/java/com/mediateeadapter/implementation/SdkProvider.kt @@ -22,9 +22,6 @@ import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCo import com.example.api.SdkServiceFactory import com.mediateeadapter.api.AbstractSandboxedSdkProviderCompat import com.mediateeadapter.api.SdkService -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch /** Provides an [SdkService] implementation when the SDK is loaded. */ class SdkProvider : AbstractSandboxedSdkProviderCompat() { @@ -35,8 +32,6 @@ class SdkProvider : AbstractSandboxedSdkProviderCompat() { private var mediatorInstance: com.example.api.SdkService? = null private var mediateeInstance: com.mediatee.api.SdkService? = null - private val coroutineScope = CoroutineScope(Dispatchers.Main) - /** * Returns the [SdkService] implementation. Called when the SDK is loaded. * @@ -64,34 +59,35 @@ class SdkProvider : AbstractSandboxedSdkProviderCompat() { /** Registers [MediateeAdapterInterface] with the Mediator. */ private fun registerWithMediator() { val controller = SdkSandboxControllerCompat.from(checkNotNull(context)) - var sandboxedSdk: SandboxedSdkCompat? = null - // Get mediatorSdk from SdkSandboxController#getSandboxedSdks. Since the adapter is - // loaded from Mediator when Mediator is loaded, Mediator sdk should be present in already - // loaded sdks. + var mediatorSdk: SandboxedSdkCompat? = null + var mediateeSdk: SandboxedSdkCompat? = null + + // Get mediatorSdk and mediateeSdk from SdkSandboxController#getSandboxedSdks. + // Since the adapter is loaded from Mediator when initialise() API is called, Mediator sdk + // should be present in already loaded sdks. + // mediateeSdk is loaded before the adapter loadSdk call is made, so it should be present + // in already loaded sdks. for (loadedSandboxedSdk in controller.getSandboxedSdks()) { - if (loadedSandboxedSdk.getSdkInfo()?.name == mediatorSdkName) { - sandboxedSdk = loadedSandboxedSdk + if (mediatorSdk == null && loadedSandboxedSdk.getSdkInfo()?.name == mediatorSdkName) { + mediatorSdk = loadedSandboxedSdk + } + if (mediateeSdk == null && loadedSandboxedSdk.getSdkInfo()?.name == mediateeSdkName) { + mediateeSdk = loadedSandboxedSdk + } + if (mediatorSdk != null && mediateeSdk != null) { break } } + mediatorInstance = - SdkServiceFactory.wrapToSdkService(checkNotNull(sandboxedSdk?.getInterface())) - // Load mediatee sdk and register MediateeAdapterInterface with Mediator. - coroutineScope.launch { - loadMediateeSdk(checkNotNull(context) { "Mediatee Sdk can't be loaded!" }) - val mediateeAdapterInterfaceImpl = MediateeAdapterInterfaceImpl( - checkNotNull(context) { "Adapter can't be registered with Mediator!" }, - checkNotNull(mediateeInstance) { "Mediatee Sdk is not loaded!" }) - mediatorInstance?.registerMediateeAdapter(mediateeAdapterInterfaceImpl) - } - } + SdkServiceFactory.wrapToSdkService(checkNotNull(mediatorSdk?.getInterface())) + mediateeInstance = + com.mediatee.api.SdkServiceFactory.wrapToSdkService(checkNotNull(mediateeSdk?.getInterface())) - private suspend fun loadMediateeSdk(context: Context) { - if (mediateeInstance == null) { - val controller = SdkSandboxControllerCompat.from(context) - val sandboxedSdk = controller.loadSdk(mediateeSdkName, Bundle.EMPTY) - mediateeInstance = - com.mediatee.api.SdkServiceFactory.wrapToSdkService(checkNotNull(sandboxedSdk.getInterface())) - } + // Register MediateeAdapterInterface with Mediator. + val mediateeAdapterInterfaceImpl = MediateeAdapterInterfaceImpl( + checkNotNull(context) { "Adapter can't be registered with Mediator!" }, + checkNotNull(mediateeInstance) { "Mediatee Sdk is not loaded!" }) + mediatorInstance?.registerMediateeAdapter(mediateeAdapterInterfaceImpl) } }