Skip to content
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
19 changes: 19 additions & 0 deletions web/android/app/src/main/java/ai/omnigent/android/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ class MainActivity : AppCompatActivity() {
switchButton.getHitRect(hitRect)
if (hitRect.height() < minHeight) hitRect.bottom = hitRect.top + minHeight
container.touchDelegate = TouchDelegate(hitRect, switchButton)
emitSwitcherHeight()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Measured server-switcher height never reaches the loaded page, so the old fixed height stays in effect

The measured pill height is pushed to the web layer (emitSwitcherHeight() at web/android/app/src/main/java/ai/omnigent/android/MainActivity.kt:586) only while the native pill is being laid out, which happens before the page finishes loading and is never repeated afterward, so the loaded page keeps using the hardcoded 25px default the change was meant to replace.
Impact: The Android scroll-fade gradient still positions itself using the old fixed height instead of the pill's real size on normal cold starts and after any page reload or server switch, so the visual bug the change targets is not actually fixed.

Layout-driven emit fires before page load and is not re-run in onPageReady

emitSwitcherHeight() sets the inline CSS custom property --omnigent-android-switcher-height on document.documentElement. It is invoked only from the pill's addOnLayoutChangeListener callback (MainActivity.kt:581-586), which runs during the activity's initial native layout pass — before webView.loadUrl(...)'s SPA has finished loading. Any inline style set against the pre-load / about:blank document is discarded when the real document loads.

Unlike the safe-area insets, which are cached in lastInsets and re-emitted from onPageReady on every pinned-origin page load (MainActivity.kt:660 calls emitInsets()), onPageReady never calls emitSwitcherHeight() (see MainActivity.kt:642-661). No native re-layout of switchButton occurs on a WebView reload or server switch (reloadWithNewServerloadUrl), so the layout listener does not fire again and the measured height is never delivered to the freshly loaded document. The CSS in web/src/index.css:167 therefore falls back to --omnigent-android-switcher-height: 25px.

Fix: cache the last measured height and also emit it from onPageReady (mirroring emitInsets), so the loaded page reliably receives the real pill height.

Prompt for agents
emitSwitcherHeight() is only called from the pill's addOnLayoutChangeListener in expandSwitchButtonTouchTarget (MainActivity.kt:586). That listener fires during the initial native layout pass, which happens before the WebView's SPA finishes loading, and it does not fire again on a page reload or server switch (reloadWithNewServer -> webView.loadUrl) because switchButton does not re-layout. As a result the inline CSS var --omnigent-android-switcher-height set on document.documentElement is lost when the real document loads, and the web layer keeps using the 25px default in web/src/index.css. Compare with emitInsets, which caches its value in lastInsets and is re-emitted from onPageReady on every pinned-origin page load so it survives reloads. Fix emitSwitcherHeight the same way: remember the last measured pill height and also emit it from onPageReady (after the origin check) so the loaded page reliably receives the measured height on cold start and after every reload/server switch.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

}
}

Expand Down Expand Up @@ -719,6 +720,24 @@ class MainActivity : AppCompatActivity() {
webView.evaluateJavascript(js, null)
}

/** Push the measured server-switcher pill height into the web layer. */
private fun emitSwitcherHeight() {
if (!::switchButton.isInitialized || !::webView.isInitialized) return
val height = switchButton.height
if (height <= 0) return
val d = resources.displayMetrics.density
val js =
"""
(() => {
document.documentElement.style.setProperty(
'--omnigent-android-switcher-height',
'${height / d}px'
);
})();
""".trimIndent()
webView.evaluateJavascript(js, null)
}

private fun hasPermission(permission: String): Boolean =
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package ai.omnigent.android

import android.content.Context
import android.content.res.Configuration
import android.view.View
import android.view.ViewGroup
import android.webkit.ValueCallback
import android.webkit.WebView
import androidx.core.view.WindowInsetsControllerCompat
import androidx.test.core.app.ApplicationProvider
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
Expand Down Expand Up @@ -118,6 +121,48 @@ class MainActivityTest {
assertEquals((48 * density).toInt(), delegate.bounds.height())
}

@Test
fun `switcher layout publishes measured height to the web layer`() {
ServerStore(ApplicationProvider.getApplicationContext()).connect("https://example.com")
val activity = Robolectric.buildActivity(MainActivity::class.java).setup().get()
val pill = activity.switchButton()
val recordingWebView = RecordingWebView(activity)
activity.replaceWebView(recordingWebView)

val density = activity.resources.displayMetrics.density
val pillHeight = (34 * density).toInt()
pill.layout(100, 40, 220, 40 + pillHeight)

assertNotNull(recordingWebView.lastScript)
assertTrue(
recordingWebView.lastScript!!.contains(
"--omnigent-android-switcher-height",
),
)
assertTrue(recordingWebView.lastScript!!.contains("34.0px"))
}

private class RecordingWebView(
context: Context,
) : WebView(context) {
var lastScript: String? = null

override fun evaluateJavascript(
script: String,
resultCallback: ValueCallback<String>?,
) {
lastScript = script
}
}

private fun MainActivity.replaceWebView(webView: WebView) {
MainActivity::class
.java
.getDeclaredField("webView")
.apply { isAccessible = true }
.set(this, webView)
}

private fun MainActivity.switchButton(): View =
MainActivity::class
.java
Expand Down
3 changes: 2 additions & 1 deletion web/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@
--omnigent-bottom-bar-visible: 0; /* web-owned 0|1 */
/* Android floating server-switcher pill footprint (margin + height).
Used by the Android scroll-fade gradient so it starts at the pill
bottom. Set once; if the pill size changes, update both values. */
bottom. Height defaults for older shells; the Android app sets
--omnigent-android-switcher-height from the measured pill after layout. */
--omnigent-android-switcher-margin: 8px;
--omnigent-android-switcher-height: 25px;
--omnigent-inset-top: var(--omnigent-safe-top);
Expand Down