using global::Android.Views;
namespace DodoSSH.Client.Android.Platform;
///
/// Hands native focus back to the terminal's WebView after Avalonia chrome took it.
///
///
///
/// The sibling of , and it exists for the same reason that one does: the
/// keyboard over a terminal belongs to the WebView's own native view, which Avalonia's focus manager does
/// not own. The accessory row's keys are already Focusable=false — see TerminalScreen — so Avalonia's
/// idea of focus never leaves the terminal when one is tapped. What still moves is Android's:
/// AvaloniaView.DispatchTouchEvent (decompiled from Avalonia.Android 12.1.1) ends every handled
/// touch — DOWN and UP alike — with a RequestFocus() for Avalonia's own view. The WebView's input
/// connection dies with its focus, the keyboard swaps to the layout it shows an editor that takes no text,
/// and the inset churn that follows can leave it sitting on top of the very row that was tapped.
///
///
/// Posted, not called — the posting is the fix's second attempt, and the first one's failure is why.
/// The first version called RequestFocus() from the keys' own Click handlers, which fire
/// inside the UP event's dispatch — and the platform's own request runs after dispatch
/// returns, so it undid ours a few microseconds later and the terminal stayed unfocused. A posted runnable
/// runs on the next main-looper message, after the platform has taken its turn, so ours is the request that
/// sticks. The focus check lives inside the posted runnable for the same reason: the answer at call time is
/// about to be made stale by the very mechanism this exists to counter.
///
///
/// The page inside the WebView never noticed any of this — its own DOM focus never moved — so regaining
/// native focus re-establishes the same input connection and the keyboard settles back to what it was.
///
///
/// Found by walking the decor view rather than asked of the NativeWebView control, because the
/// control does not expose its platform child and this application only ever has the one WebView — the
/// walk's first match is necessarily the terminal. Every step is allowed to be absent, exactly as
/// 's are: no activity while backgrounded, no WebView while the terminal
/// surface has never been shown, and nothing to do in either case.
///
///
internal static class TerminalFocus
{
public static void Return()
{
if (PhoneEnvironment.CurrentActivity?.Window?.DecorView is not ViewGroup decor)
{
return;
}
if (FindWebView(decor) is not { } webView)
{
return;
}
webView.Post(() =>
{
if (!webView.IsFocused)
{
webView.RequestFocus();
}
});
}
private static View? FindWebView(ViewGroup parent)
{
for (var i = 0; i < parent.ChildCount; i++)
{
switch (parent.GetChildAt(i))
{
case global::Android.Webkit.WebView webView:
return webView;
case ViewGroup child when FindWebView(child) is { } found:
return found;
}
}
return null;
}
}