From 90150e72e46224e1812afd13994d02b176f3edfb Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 23 Jun 2026 23:16:02 +0000 Subject: [PATCH] fix: upgrade privacysandbox.ui to alpha17 (embedded drag/scroll input) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The embedded surface forwarding taps but cancelling drags was a known bug in androidx.privacysandbox.ui alpha10: with the provider surface z-ordered above, "the gesture is exclusively received by the provider window and not transferred to the client window" (alpha13 release notes). alpha15 then "set the default Z-ordering to below" and "added support for the UI provider to receive MotionEvents in this mode after being received by the client window" — i.e. the drag-input path we needed. Bump alpha10 → alpha17 and adapt the changed API: openSession takes SessionData instead of a windowInputToken IBinder, Session adds notifySessionRendered, and the session-state listener became setEventListener(SandboxedSdkViewEventListener) (ready now flips on onUiDisplayed). The direct-WebView browser is unaffected (it doesn't use this library). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01MgMpRcWj6y82LxLiwcuzmN --- .../browser/EmbeddedBrowserController.kt | 17 ++++++++++------- .../favorites/EmbeddedNappletController.kt | 17 ++++++++++------- gradle/libs.versions.toml | 2 +- .../napplethost/NappletBrowserUiAdapter.kt | 6 ++++-- .../napplethost/NappletHostUiAdapter.kt | 6 ++++-- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/browser/EmbeddedBrowserController.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/browser/EmbeddedBrowserController.kt index 02750305df..fc12fdfa1e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/browser/EmbeddedBrowserController.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/browser/EmbeddedBrowserController.kt @@ -35,9 +35,8 @@ import androidx.annotation.RequiresApi import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory -import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionState -import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionStateChangedListener import androidx.privacysandbox.ui.client.view.SandboxedSdkView +import androidx.privacysandbox.ui.client.view.SandboxedSdkViewEventListener import androidx.privacysandbox.ui.core.SandboxedUiAdapter import com.vitorpamplona.amethyst.napplethost.NappletBrowserContract import com.vitorpamplona.amethyst.ui.screen.loggedIn.embed.EmbeddedSurfaceController @@ -105,12 +104,16 @@ class EmbeddedBrowserController( // Paint the surface placeholder in the app's theme background so there's no white flash before // the remote WebView delivers its first frame. view.setBackgroundColor(backgroundColor) - // Flip ready once the session opens, so callers can drop their loading placeholder. - view.addStateChangedListener( - object : SandboxedSdkUiSessionStateChangedListener { - override fun onStateChanged(state: SandboxedSdkUiSessionState) { - if (state is SandboxedSdkUiSessionState.Active) readyState.value = true + // Flip ready once the remote UI is displayed, so callers can drop their loading placeholder. + view.setEventListener( + object : SandboxedSdkViewEventListener { + override fun onUiDisplayed() { + readyState.value = true } + + override fun onUiError(error: Throwable) {} + + override fun onUiClosed() {} }, ) pendingAdapter?.let { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/favorites/EmbeddedNappletController.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/favorites/EmbeddedNappletController.kt index b3670f1a9b..6110382f1e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/favorites/EmbeddedNappletController.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/favorites/EmbeddedNappletController.kt @@ -35,9 +35,8 @@ import androidx.annotation.RequiresApi import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory -import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionState -import androidx.privacysandbox.ui.client.view.SandboxedSdkUiSessionStateChangedListener import androidx.privacysandbox.ui.client.view.SandboxedSdkView +import androidx.privacysandbox.ui.client.view.SandboxedSdkViewEventListener import androidx.privacysandbox.ui.core.SandboxedUiAdapter import com.vitorpamplona.amethyst.napplethost.NappletEmbedContract import com.vitorpamplona.amethyst.napplethost.NappletHostContract @@ -106,12 +105,16 @@ class EmbeddedNappletController( // Paint the surface placeholder in the app's theme background so there's no white flash before // the remote WebView delivers its first frame. view.setBackgroundColor(params.getInt(NappletHostContract.EXTRA_BG_COLOR, android.graphics.Color.WHITE)) - // Flip ready once the session opens, so callers can drop their loading placeholder. - view.addStateChangedListener( - object : SandboxedSdkUiSessionStateChangedListener { - override fun onStateChanged(state: SandboxedSdkUiSessionState) { - if (state is SandboxedSdkUiSessionState.Active) readyState.value = true + // Flip ready once the remote UI is displayed, so callers can drop their loading placeholder. + view.setEventListener( + object : SandboxedSdkViewEventListener { + override fun onUiDisplayed() { + readyState.value = true } + + override fun onUiError(error: Throwable) {} + + override fun onUiClosed() {} }, ) pendingAdapter?.let { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 05b6904330..05adc89478 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -78,7 +78,7 @@ zxing = "3.5.4" zxingAndroidEmbedded = "4.3.0" webkit = "1.12.1" # Cross-process UI embedding (SurfaceControlViewHost wrapper) for the in-app browser surface. Apache-2.0. -privacysandboxUi = "1.0.0-alpha10" +privacysandboxUi = "1.0.0-alpha17" windowCoreAndroid = "1.5.1" workRuntime = "2.11.2" androidxCamera = "1.6.1" diff --git a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletBrowserUiAdapter.kt b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletBrowserUiAdapter.kt index 4e643928f7..ddbaf496cb 100644 --- a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletBrowserUiAdapter.kt +++ b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletBrowserUiAdapter.kt @@ -25,13 +25,13 @@ import android.content.res.Configuration import android.os.Build import android.os.Bundle import android.os.Handler -import android.os.IBinder import android.os.Looper import android.view.View import android.webkit.WebView import android.widget.FrameLayout import androidx.annotation.RequiresApi import androidx.privacysandbox.ui.core.SandboxedUiAdapter +import androidx.privacysandbox.ui.core.SessionData import androidx.privacysandbox.ui.provider.AbstractSandboxedUiAdapter import java.util.concurrent.Executor @@ -48,7 +48,7 @@ class NappletBrowserUiAdapter( override fun openSession( context: Context, - windowInputToken: IBinder, + sessionData: SessionData, initialWidth: Int, initialHeight: Int, isZOrderOnTop: Boolean, @@ -79,6 +79,8 @@ private class BrowserSession( override val signalOptions: Set = emptySet() + override fun notifySessionRendered(supportedSignalOptions: Set) {} + override fun notifyResized( width: Int, height: Int, diff --git a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletHostUiAdapter.kt b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletHostUiAdapter.kt index 736e187002..c2d5f94e32 100644 --- a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletHostUiAdapter.kt +++ b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletHostUiAdapter.kt @@ -25,13 +25,13 @@ import android.content.res.Configuration import android.os.Build import android.os.Bundle import android.os.Handler -import android.os.IBinder import android.os.Looper import android.view.View import android.webkit.WebView import android.widget.FrameLayout import androidx.annotation.RequiresApi import androidx.privacysandbox.ui.core.SandboxedUiAdapter +import androidx.privacysandbox.ui.core.SessionData import androidx.privacysandbox.ui.provider.AbstractSandboxedUiAdapter import java.util.concurrent.Executor @@ -49,7 +49,7 @@ class NappletHostUiAdapter( override fun openSession( context: Context, - windowInputToken: IBinder, + sessionData: SessionData, initialWidth: Int, initialHeight: Int, isZOrderOnTop: Boolean, @@ -80,6 +80,8 @@ private class HostSession( override val signalOptions: Set = emptySet() + override fun notifySessionRendered(supportedSignalOptions: Set) {} + override fun notifyResized( width: Int, height: Int,