SandboxedUiAdapterFactory.kt
/*
* Copyright 2022 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.
*/
package androidx.privacysandbox.ui.client
import android.content.Context
import android.content.res.Configuration
import android.hardware.display.DisplayManager
import android.os.Build
import android.os.Bundle
import android.os.IBinder
import android.view.Display
import android.view.SurfaceControlViewHost
import android.view.SurfaceView
import android.view.View
import androidx.annotation.RequiresApi
import androidx.privacysandbox.ui.core.IRemoteSessionClient
import androidx.privacysandbox.ui.core.IRemoteSessionController
import androidx.privacysandbox.ui.core.ISandboxedUiAdapter
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
import java.util.concurrent.Executor
/**
* Provides an adapter created from a supplied Bundle which acts as a proxy between the host app and
* the Binder provided by the provider of content.
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
object SandboxedUiAdapterFactory {
// Bundle key is a binary compatibility requirement
private const val UI_ADAPTER_BINDER = "uiAdapterBinder"
/**
* @throws IllegalArgumentException if {@code coreLibInfo} does not contain a Binder with the
* key UI_ADAPTER_BINDER
*/
fun createFromCoreLibInfo(coreLibInfo: Bundle): SandboxedUiAdapter {
val uiAdapterBinder = requireNotNull(coreLibInfo.getBinder(UI_ADAPTER_BINDER)) {
"Invalid bundle, missing $UI_ADAPTER_BINDER."
}
val adapterInterface = ISandboxedUiAdapter.Stub.asInterface(
uiAdapterBinder
)
return RemoteAdapter(adapterInterface)
}
private class RemoteAdapter(private val adapterInterface: ISandboxedUiAdapter) :
SandboxedUiAdapter {
override fun openSession(
context: Context,
windowInputToken: IBinder,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
clientExecutor: Executor,
client: SandboxedUiAdapter.SessionClient
) {
val mDisplayManager =
context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
val displayId = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).displayId
adapterInterface.openRemoteSession(
windowInputToken,
displayId,
initialWidth,
initialHeight,
isZOrderOnTop,
RemoteSessionClient(context, client, clientExecutor)
)
}
class RemoteSessionClient(
val context: Context,
val client: SandboxedUiAdapter.SessionClient,
val clientExecutor: Executor
) : IRemoteSessionClient.Stub() {
override fun onRemoteSessionOpened(
surfacePackage: SurfaceControlViewHost.SurfacePackage,
remoteSessionController: IRemoteSessionController,
isZOrderOnTop: Boolean
) {
val surfaceView = SurfaceView(context)
surfaceView.setChildSurfacePackage(surfacePackage)
surfaceView.setZOrderOnTop(isZOrderOnTop)
clientExecutor.execute {
client.onSessionOpened(SessionImpl(surfaceView, remoteSessionController))
}
}
override fun onRemoteSessionError(errorString: String) {
clientExecutor.execute {
client.onSessionError(Throwable(errorString))
}
}
override fun onResizeRequested(width: Int, height: Int) {
clientExecutor.execute {
client.onResizeRequested(width, height)
}
}
}
private class SessionImpl(
val surfaceView: SurfaceView,
val remoteSessionController: IRemoteSessionController
) : SandboxedUiAdapter.Session {
override val view: View = surfaceView
override fun notifyConfigurationChanged(configuration: Configuration) {
remoteSessionController.notifyConfigurationChanged(configuration)
}
override fun notifyResized(width: Int, height: Int) {
remoteSessionController.notifyResized(width, height)
}
override fun notifyZOrderChanged(isZOrderOnTop: Boolean) {
surfaceView.setZOrderOnTop(isZOrderOnTop)
remoteSessionController.notifyZOrderChanged(isZOrderOnTop)
}
override fun close() {
remoteSessionController.close()
}
}
}
}