diff --git a/SteUp.Maui/Platforms/Android/MainActivity.cs b/SteUp.Maui/Platforms/Android/MainActivity.cs index 9e73182..9d5aaa8 100644 --- a/SteUp.Maui/Platforms/Android/MainActivity.cs +++ b/SteUp.Maui/Platforms/Android/MainActivity.cs @@ -1,14 +1,49 @@ using Android.App; using Android.Content.PM; +using Android.OS; +using Android.Views; +using AndroidX.Core.View; namespace SteUp.Maui { [Activity( Theme = "@style/Maui.SplashTheme", MainLauncher = true, + WindowSoftInputMode = SoftInput.AdjustResize | SoftInput.StateHidden, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] public class MainActivity : MauiAppCompatActivity { + protected override void OnCreate(Bundle? savedInstanceState) + { + base.OnCreate(savedInstanceState); + + // Da Android 15 (API 35) il edge-to-edge e' forzato e adjustResize non ridimensiona + // piu' la finestra: la tastiera si limita a coprire la WebView. Riduciamo noi il + // content view dell'altezza dell'IME cosi' il layout Blazor (100vh) si ricalcola. + if (FindViewById(Android.Resource.Id.Content) is { } content) + ViewCompat.SetOnApplyWindowInsetsListener(content, new ImeInsetsListener()); + } + + private sealed class ImeInsetsListener : Java.Lang.Object, IOnApplyWindowInsetsListener + { + public WindowInsetsCompat OnApplyWindowInsets(Android.Views.View? v, WindowInsetsCompat? insets) + { + if (v is null || insets is null) + return insets!; + + var ime = insets.GetInsets(WindowInsetsCompat.Type.Ime()).Bottom; + var systemBars = insets.GetInsets(WindowInsetsCompat.Type.SystemBars()).Bottom; + + // La barra di navigazione e' gia' gestita da SafeAreaEdges in MainPage: + // teniamo solo la parte di tastiera che la eccede, per non sommare due volte. + var bottom = System.Math.Max(0, ime - systemBars); + + if (v.PaddingBottom != bottom) + v.SetPadding(v.PaddingLeft, v.PaddingTop, v.PaddingRight, bottom); + + return insets; + } + } } } diff --git a/SteUp.Shared/wwwroot/js/main.js b/SteUp.Shared/wwwroot/js/main.js index fbd9b20..ea1c5d7 100644 --- a/SteUp.Shared/wwwroot/js/main.js +++ b/SteUp.Shared/wwwroot/js/main.js @@ -95,3 +95,38 @@ document.addEventListener("DOMContentLoaded", () => { }, 600); } })(); + + +// Tastiera: porta il campo attivo dentro l'area visibile. +// Il layout e' a 100vh con overflow:hidden, quindi quando l'host riduce la +// WebView per la tastiera il browser non scrolla da solo sul campo col focus. +(function () { + const SELECTOR = 'input, textarea, [contenteditable="true"]'; + let pending = null; + + function scrollFocusedIntoView() { + const el = document.activeElement; + if (!el || !el.matches || !el.matches(SELECTOR)) return; + el.scrollIntoView({block: 'center', behavior: 'smooth'}); + } + + function schedule() { + clearTimeout(pending); + // attende la fine dell'animazione di apertura della tastiera e del relayout + pending = setTimeout(scrollFocusedIntoView, 300); + } + + document.addEventListener('focusin', function (event) { + if (event.target && event.target.matches && event.target.matches(SELECTOR)) { + schedule(); + } + }); + + // Il resize arriva quando la tastiera si apre/chiude o cambia altezza + // (es. passaggio a tastiera emoji o suggerimenti). + if (window.visualViewport) { + window.visualViewport.addEventListener('resize', schedule); + } else { + window.addEventListener('resize', schedule); + } +})();