Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Banner Ad implementation for InApp Mediatee. #125

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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 @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.example.api

import android.os.Bundle
import androidx.privacysandbox.tools.PrivacySandboxService

@PrivacySandboxService
Expand All @@ -23,7 +24,7 @@ interface SdkService {

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ 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!"
Expand Down Expand Up @@ -63,11 +63,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,
Expand All @@ -79,7 +89,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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
9 changes: 9 additions & 0 deletions PrivacySandboxKotlin/inapp-mediatee-sdk-adapter/README.md
Original file line number Diff line number Diff line change
@@ -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).
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package com.inappmediateeadapter.implementation

import android.content.Context
anaghask-google marked this conversation as resolved.
Show resolved Hide resolved
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
import java.com.inappmediateeadapter.implementation.SandboxedUiAdapterImpl

/**
* Adapter class that implements the interface declared by the Mediator.
Expand All @@ -20,7 +22,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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package java.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<String> = 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.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="4dp"
android:gravity="center_horizontal"
android:background="@android:color/holo_purple">

<LinearLayout
android:id="@+id/ad_layout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:background="@android:color/background_light">

<TextView
android:id="@+id/banner_header_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="8dp"
android:textColor="@android:color/black"
android:text="@string/banner_ad_label"/>
</LinearLayout>
</LinearLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@
-->
<resources>
<string name="activity_label">Full screen ad launched by in-app mediatee!</string>
<string name="banner_ad_label">Ad from In-app Mediatee SDK (no overlay from mediator)</string>
</resources>