Public Access
Tell the phone's keyboard these are secrets, and get it off the box
Five boxes on this head take a secret and every one of them was drawing dots and saying nothing. `PasswordChar` is a screen property: Windows has no opinion about what is being typed into a text box, so the desktop head needs nothing more. Android's software keyboard has an opinion, and left at its default it read a vault passphrase as prose — completions offered in the suggestion strip above the box, and the passphrase itself learned into the IME's dictionary. Dots on screen with a word bar over them is the worst of both: hidden from the person typing it and offered to the room. `TextInputOptions.ContentType` is the property the Android backend maps onto `InputType`, and it is what turns both off. Both attributes now live in one `TextBox.secret` class rather than being repeated per box, because they are two halves of one fact and the next box added would have got one of them. The keyboard also went on covering whichever box had raised it. That is in `PhoneShell` rather than on each screen: everything the phone draws is inside its one root panel, so a bottom margin shortens all eleven screens at once, and a screen added later cannot forget to handle something it never had to know about. Two mechanisms, and it matters that neither is a backstop for the other. Before Android 15 the activity now declares `AdjustResize` and the platform shortens the window itself; left unspecified Android chooses, and what it chooses for a window whose entire content is one native view — which is what an Avalonia surface is — is to pan, sliding the window by however much it thinks the focused native view needs and leaving the box exactly where it was. That was the bug. From Android 15 the attribute is ignored, edge-to-edge being enforced and the window no longer resized for the keyboard at all, and the reported inset is what there is. Each is dead where the other applies — where the window resizes, the inset arrives already consumed and measures zero — which is why the margin comes from the inset alone. Both added together would strand the interface an entire keyboard above the keyboard. Scrolling the box back into view keys off the size change rather than off either mechanism. `ScrollViewer` already brings a newly focused child into view; what it cannot know is that the visible region shrank after the focus, and both ways of losing that region end in the same resize. None of it is reachable by a test. The software keyboard is an inset the platform reports and a headless top level reports none, so phase 10 of `docs/manual-checks.md` is the whole of the verification — including the note to run it on one device each side of Android 15, since a build exercised on only one of the two will look correct and be half broken.
This commit is contained in:
@@ -8,6 +8,7 @@ using DodoSSH.Client.Android.Platform;
|
||||
using global::Android.App;
|
||||
using global::Android.Content;
|
||||
using global::Android.Content.PM;
|
||||
using global::Android.Views;
|
||||
|
||||
namespace DodoSSH.Client.Android;
|
||||
|
||||
@@ -34,12 +35,26 @@ namespace DodoSSH.Client.Android;
|
||||
/// other launch mode answers it with a second copy of this activity on top of the first — which on this
|
||||
/// head would mean a second Avalonia application over a live one.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b><c>AdjustResize</c> is declared rather than left unspecified</b>, and it is half of how this head
|
||||
/// keeps the software keyboard off the box being typed into; <c>PhoneShell</c> is the other half. Left
|
||||
/// unspecified, Android chooses, and what it chooses for a window whose entire content is one native view
|
||||
/// — which is what an Avalonia surface is — is to pan: it slides the window up by however much it thinks
|
||||
/// the focused *native* view needs, and since that view is the whole surface, the passphrase box goes on
|
||||
/// sitting under the keyboard. Resizing instead makes the window shorter, which the layout inside it can
|
||||
/// answer, and a screen built around a <c>ScrollViewer</c> then scrolls the focused box into view by
|
||||
/// itself. On Android 15 and later this attribute is ignored — edge-to-edge is enforced there and the
|
||||
/// window is no longer resized for the keyboard — which is precisely the case PhoneShell handles from the
|
||||
/// reported inset. The two are complementary and never both in effect: where the window resizes, the
|
||||
/// keyboard inset arrives already consumed and measures zero.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[Activity(
|
||||
Label = "DodoSSH",
|
||||
Theme = "@style/DodoTheme",
|
||||
MainLauncher = true,
|
||||
LaunchMode = LaunchMode.SingleTask,
|
||||
WindowSoftInputMode = SoftInput.AdjustResize,
|
||||
ConfigurationChanges = ConfigChanges.Orientation
|
||||
| ConfigChanges.ScreenSize
|
||||
| ConfigChanges.ScreenLayout
|
||||
|
||||
Reference in New Issue
Block a user