Key concepts | Set up your development environment | Build an RE SDK | Consume the RE SDK | Testing, and building for distribution |
Consume the runtime-enabled SDK
This section describes how clients can interact with the declared runtime-enabled (RE) SDK APIs.
In the case of this guide, we refer to your existing SDK module (or runtime-aware SDK) as the client.
If you want to the runtime-enabled SDK directly into your app, the app module is the client.
Load the runtime-enabled SDK
The first thing you want to do on your runtime-aware SDK or client app is load the runtime-enabled SDK.
The SdkSandboxManager
class assists with loading runtime-enabled SDKs, returning an IBinder
class
that the runtime-aware SDK can bind to the interface declared in the
runtime-enabled SDK.
You need to ensure that you only load each runtime-enabled SDK once, or the SDK manager will return an exception.
The shim generation tools generate helper classes to convert the IBinder
interface returned by the SdkSandboxManager
back to the declared SDK API interface.
The tools use the interface annotated with @PrivacySandboxService
to generate
a *Factory
class.
This class contains a static wrapTo*
function that converts an IBinder
object into an instance of your runtime-enabled SDK's interface.
Your runtime-aware SDK can communicate with the runtime-enabled SDK using this interface, and invoke the SDK APIs you declared in the previous step.
// Name of the SDK to be loaded, defined in your ASB module
private const val SDK_NAME = "com.example.sdk"
try {
// SdkSandboxManagerCompat is used to communicate with the sandbox and load SDKs with backward compatibility.
val sandboxManagerCompat = SdkSandboxManagerCompat.from(context)
val sandboxedSdk = sandboxManagerCompat.loadSdk(SDK_NAME, Bundle.EMPTY)
val mySdk = MySdkFactory.wrapToMySdk(sandboxedSdk.getInterface()!!)
} catch (e: LoadSdkCompatException) {
Log.e(TAG, "Failed to load SDK, error code: ${e.loadSdkErrorCode}", e)
return null
}
UI library usage
If you want to use the UI library to display ads, make sure you have added
androidx.privacysandbox.ui:ui-core
and androidx.privacysandbox.ui:ui-client
to the dependencies in the build.gradle of your runtime-aware SDK.
Load a banner ad using SandboxedSdkView
androidx.privacysandbox.ui:ui-client
introduces a new ViewGroup
called
SandboxedSdkView
to host UI created by a runtime-enabled SDK.
setAdapter()
opens a session with the runtime-enabled SDK to
receive the ad view and notifications of UI changes. When the SDK opens the
session, the ad is shown.
This could be integrated as follows:
class BannerAd(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
suspend fun loadAd() {
// mySdk is the previously loaded SDK in the SDK Runtime.
val bannerAd = mySdk.loadAd()
val sandboxedSdkView = SandboxedSdkView(context)
addViewToLayout(sandboxedSdkView)
// This renders the ad.
sandboxedSdkView.setAdapter(bannerAd)
return
}
private fun addViewToLayout(view: View) {
view.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
super.addView(view)
}
}
Your runtime-aware SDK can also get notified when the session state changes for the UI presentation. To do this:
Create a
SessionStateChangeListener()
class to handle the different scenarios:private class SessionStateChangeListener() : SandboxedSdkUiSessionStateChangedListener { override fun onStateChanged(state: SandboxedSdkUiSessionState) { if (state is SandboxedSdkUiSessionState.Error) { // Some error has occurred while opening the session. Handle // accordingly. Log.e(TAG, state.throwable.message!!); } else if (state is SandboxedSdkUiSessionState.Loading) { // The session is attempting to be opened. } else if (state is SandboxedSdkUiSessionState.Active) { // The session is open and the UI presentation was successful. } else if (state is SandboxedSdkUiSessionState.Idle) { // There is no open session. } } }
Add a state change listener to the
SandboxedSdkView
you've instantiated earlier on. The listener is immediately called with the current state as soon as it's attached to the view.
Note the following:
- If the runtime-aware SDK calls
SandboxedSdkView
methods when the session still has not finished opening, all effects will be applied after the session has finished opening.- Methods such as SandboxedSdkView.orderProviderUiAboveClientUi(providerUiOnTop)
- Calling methods that add or remove a view from
SandboxedSdkView
(such asaddView()
,removeView()
,removeViewAt()
, etc.) is not supported, throwing anUnsupportedOperationException
.- Only use
setAdapter()
to show the ad.
- Only use
SandboxedSdkView.orderProviderUiAboveClientUi(providerUiOnTop)
toggles Z ordering which affects whetherMotionEvents
from user interaction get sent to the runtime-enabled SDK or the runtime-aware SDK.- If set to
false
, theMotionEvents
are sent to the runtime-aware SDK, otherwise they'll get sent to the runtime-enabled SDK. Learn more about Z ordering using UI Presentation APIs.
- If set to
Start activities
To start activities owned by the runtime-enabled SDK, use the createSdkActivityLauncher
extension to create a launcher in the runtime-aware SDK.
This launcher can then be passed to your runtime-enabled SDK, allowing it to initiate activities as needed.
You can use a predicate to control whether the activity will be launched or not.
The predicate needs to return a true
value for activities to be allowed.
val launchSdkActivityPredicate = {
// Boolean which has to be true to launch the activities
}
val launcher = baseActivity.createSdkActivityLauncher(launchSdkActivityPredicate)
fullscreenService.showActivity(launcher)
Within your runtime-enabled SDK, register SdkSandboxActivityHandlerCompat
,
and provide it to SdkActivityLauncher.LaunchSdkActivity(IBinder)
.
fun showActivity(activityLauncher: SdkActivityLauncher) {
val handler = object : SdkSandboxActivityHandlerCompat {
override fun onActivityCreated(activityHolder: ActivityHolder) {
activityHolder.getActivity().setContentView(contentView)
}
}
val token = controller.registerSdkSandboxActivityHandler(handler)
activityLauncher.launchSdkActivity(token)
}
The ActivityHolder
passed to
SdkSandboxActivityHandlerCompat.onActivityCreated(ActivityHolder)
implements
LifecycleOwner
, giving your runtime-enabled SDK access to the activity's lifecycle.
It also provides the getOnBackPressedDispatcher
API, which can be used to
register getOnBackPressedCallback
instances to handle the back button behavior
within the activity.
Step 3: Build a runtime-enabled SDKStep 5: Testing and building for distribution