diff --git a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletBrowserActivity.kt b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletBrowserActivity.kt index ef053427ea..c79b04f2b7 100644 --- a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletBrowserActivity.kt +++ b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletBrowserActivity.kt @@ -631,7 +631,7 @@ class NappletBrowserActivity : ComponentActivity() { onInfo = null, liveUrl = startUrl, onNavigate = { loadAddress(it) }, - onConsole = { consolePanel?.toggle() }, + onConsole = { show -> consolePanel?.setShowing(show) }, isFavoriteInitially = intent.getBooleanExtra(EXTRA_IS_FAVORITE, false), onFavoriteToggle = { url, _ -> sendFavoriteToggle(url) }, ).also { controlSheet = it } diff --git a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletConsolePanel.kt b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletConsolePanel.kt index d9e6d386d0..8c461caf09 100644 --- a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletConsolePanel.kt +++ b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletConsolePanel.kt @@ -37,11 +37,17 @@ import androidx.core.content.ContextCompat import com.vitorpamplona.amethyst.commons.R as CommonsR /** - * The full-screen browser's **bottom pull-up sheet** for JavaScript console output. Collapsed it's - * a small grabber at the bottom edge, symmetric to [NappletControlSheet]'s top grabber. Pull it up - * (or tap) to reveal a scrollable log of [console.log / warn / error / debug] messages captured - * from the page via `WebChromeClient.onConsoleMessage`. Capped at [MAX_ENTRIES] entries (oldest - * dropped on overflow). Built in plain Views like [NappletControlSheet] — no Compose/Material. + * The full-screen browser's **bottom pull-up sheet** for JavaScript console output. The whole sheet is + * hidden until the user flips the Console **toggle** in [NappletControlSheet] ([setShowing]); turned on, + * it reveals a small grabber at the bottom edge (symmetric to that sheet's top grabber) already pulled + * up. Pull it down/up (or tap) to collapse/expand the scrollable log of + * [console.log / warn / error / debug] messages captured from the page via + * `WebChromeClient.onConsoleMessage`. Capped at [MAX_ENTRIES] entries (oldest dropped on overflow). + * Built in plain Views like [NappletControlSheet] — no Compose/Material. + * + * Its grabber + panel are elevated above [NappletControlSheet]'s panel so that, when both are open at + * once (e.g. in landscape), this bottom sheet draws on top of the top pull-down sheet — mirroring the + * Compose layer, where `BottomConsoleSheet` is composed after `TopControlSheet`. */ @SuppressLint("UseSwitchCompatOrMaterialCode") class NappletConsolePanel( @@ -52,6 +58,7 @@ class NappletConsolePanel( private val surface = resolveThemeColor(android.R.attr.colorBackground) private var expanded = false + private var showing = false private val panel: LinearLayout private lateinit var logContainer: LinearLayout private lateinit var scrollView: ScrollView @@ -59,6 +66,9 @@ class NappletConsolePanel( init { orientation = VERTICAL gravity = Gravity.CENTER_HORIZONTAL + // Hidden until the Console toggle turns it on; matches the Compose `BottomConsoleSheet`, which is + // only composed while the toggle is on. + visibility = View.GONE panel = buildPanel().also { addView(it) } addView(buildGrabber()) @@ -68,7 +78,8 @@ class NappletConsolePanel( LinearLayout(context).apply { orientation = VERTICAL visibility = View.GONE - elevation = dp(6).toFloat() + // Above NappletControlSheet's panel (6dp) so an open console draws over an open top sheet. + elevation = dp(8).toFloat() background = GradientDrawable().apply { cornerRadii = floatArrayOf(dp(16).toFloat(), dp(16).toFloat(), dp(16).toFloat(), dp(16).toFloat(), 0f, 0f, 0f, 0f) @@ -123,9 +134,21 @@ class NappletConsolePanel( var entryCount: Int = 0 private set - /** Toggles the panel's expanded/collapsed state; used by the control sheet's Console row. */ - fun toggle() { - if (expanded) collapse() else expand() + /** + * Shows or hides the entire sheet (grabber + log), driven by the control sheet's Console **toggle**: + * off hides everything, on reveals the sheet already pulled up — mirroring the Compose + * `BottomConsoleSheet`, which is only composed while the toggle is on and opens expanded. + */ + fun setShowing(show: Boolean) { + if (show == showing) return + showing = show + if (show) { + visibility = View.VISIBLE + expand() + } else { + collapse() + visibility = View.GONE + } } fun appendLog( @@ -205,6 +228,8 @@ class NappletConsolePanel( gravity = Gravity.CENTER_HORIZONTAL } setPadding(dp(16), dp(7), dp(16), dp(7)) + // Above NappletControlSheet's panel (6dp) so the grabber stays on top of an open top sheet. + elevation = dp(8).toFloat() background = GradientDrawable().apply { cornerRadii = floatArrayOf(dp(12).toFloat(), dp(12).toFloat(), dp(12).toFloat(), dp(12).toFloat(), 0f, 0f, 0f, 0f) diff --git a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletControlSheet.kt b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletControlSheet.kt index dc329efa66..605909c7a6 100644 --- a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletControlSheet.kt +++ b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletControlSheet.kt @@ -65,9 +65,9 @@ class NappletControlSheet( // nsite/napplet), where it renders an editable address row; [onNavigate] loads what the user types. liveUrl: String? = null, private val onNavigate: ((String) -> Unit)? = null, - // When non-null, a "Console" row is added to the pull-down sheet. The callback toggles the - // browser's console log panel; the count label is updated via [updateConsoleCount]. - private val onConsole: (() -> Unit)? = null, + // When non-null, a "Console" toggle row is added to the pull-down sheet. The callback is invoked with + // the new visibility each time the user flips it; the count label is updated via [updateConsoleCount]. + private val onConsole: ((Boolean) -> Unit)? = null, // When non-null, a favorite toggle row is shown; called with the current URL and new isFavorite state. isFavoriteInitially: Boolean = false, private val onFavoriteToggle: ((url: String, isFavorite: Boolean) -> Unit)? = null, @@ -80,6 +80,7 @@ class NappletControlSheet( private var torOn = torInitiallyOn private var currentUrl = liveUrl private var isFavorite = isFavoriteInitially + private var consoleShowing = false private val panel: LinearLayout private var torLabel: TextView? = null @@ -87,6 +88,7 @@ class NappletControlSheet( private var addressField: EditText? = null private var securityGlyph: TextView? = null private var consoleLabel: TextView? = null + private var consoleSwitch: Switch? = null private var favoriteLabel: TextView? = null init { @@ -129,25 +131,32 @@ class NappletControlSheet( }, ) } - onConsole?.let { console -> + onConsole?.let { val label = TextView(context).apply { text = context.getString(CommonsR.string.browser_console_title_short) setTextColor(onSurface) textSize = 15f setPadding(dp(8), 0, 0, 0) + // Weight 1 so the label fills and shoves the Switch to the end, like the Tor row. + layoutParams = LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f) } consoleLabel = label + // Display-only switch (the whole row is the touch target), matching the Tor row + Compose twin. + val toggle = + Switch(context).apply { + isChecked = consoleShowing + isClickable = false + isFocusable = false + } + consoleSwitch = toggle addView( LinearLayout(context).apply { orientation = HORIZONTAL gravity = Gravity.CENTER_VERTICAL setPadding(dp(8), dp(10), dp(8), dp(10)) isClickable = true - setOnClickListener { - collapse() - console() - } + setOnClickListener { toggleConsole() } addView( TextView(context).apply { text = ">" @@ -155,10 +164,11 @@ class NappletControlSheet( textSize = 18f width = dp(28) gravity = Gravity.CENTER - typeface = android.graphics.Typeface.MONOSPACE + typeface = Typeface.MONOSPACE }, ) addView(label) + addView(toggle) }, ) } @@ -367,6 +377,14 @@ class NappletControlSheet( onToggleTor(next) } + private fun toggleConsole() { + consoleShowing = !consoleShowing + consoleSwitch?.isChecked = consoleShowing + // Collapse the top sheet on toggle, like the Compose twin, so the bottom console isn't hidden behind it. + collapse() + onConsole?.invoke(consoleShowing) + } + private fun actionRow( glyph: String, label: String, diff --git a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletHostActivity.kt b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletHostActivity.kt index 4861ab7798..3a486e9407 100644 --- a/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletHostActivity.kt +++ b/nappletHost/src/main/kotlin/com/vitorpamplona/amethyst/napplethost/NappletHostActivity.kt @@ -838,7 +838,7 @@ class NappletHostActivity : ComponentActivity() { torInitiallyOn = if (profile.exposesNetwork && proxyPort > 0) useTor else null, onNetworkTap = if (profile.exposesNetwork && proxyPort > 0) ({ setNetworkMode(!useTor) }) else null, onInfo = { showAccessDialog() }, - onConsole = { consolePanel?.toggle() }, + onConsole = { show -> consolePanel?.setShowing(show) }, ).also { controlSheet = it } private fun buildConsolePanel(): View =