Public Access
Give the phone the rest of its screens, and a way in
All seven screens of the design, plus the two it does not draw because it starts at an enrolled phone: naming a server, and choosing a passphrase. The five states docs/android-port.md worried about losing at 360dp are all here and none of them softened. The changed-key refusal is a full-screen panel rather than a bottom sheet, because a sheet is swipe-to-dismiss by convention and that screen must have no way forward. The recovery code raises FLAG_SECURE for its own state and lowers it afterwards, so the sentence about screenshots is true rather than decorative. The delete confirmations keep their counts and replace the row in place. Signing in works, and the seam it needed is worth more than the implementation: IAuthorizationCallback now sits between OidcClient and the loopback listener, so the two heads differ in where the response arrives and in nothing else. PKCE, the state check, discovery, the token exchange and the key binding stay one implementation — a second OIDC client would be a second place for a security bug to live. The phone registers a private-use scheme with the system rather than binding a loopback port, which on a shared device any other app can do first. The accessory key row needed TerminalWorkspace.SendInputAsync: ordinary typing goes from the renderer straight down the socket, and there was no way in for the keys a software keyboard does not have. Ctrl latches, because one thumb cannot chord, and the latch is drawn — a modifier that is on and does not look on is how somebody sends ^L to a database prompt believing they typed an l. 597 client tests green, including two new ones for the input path and one for the terminal surface command. Nothing has run on a device.
This commit is contained in:
1 parent
81e7e6d939
commit
7a3a521c59
51 files changed
+2144
-134
No files matched your search
@@ -0,0 +1,119 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
|
||||
// Avalonia's Button, not Android.Widget's. .NET for Android puts Android.Widget in this project's
|
||||
// implicit usings, so the bare name is ambiguous — the same collision the namespace itself causes
|
||||
// for Android.App. See the note at the top of MainActivity.
|
||||
using Application = Avalonia.Application;
|
||||
using Button = Avalonia.Controls.Button;
|
||||
|
||||
namespace DodoSSH.Client.Android.Views;
|
||||
|
||||
/// <summary>
|
||||
/// One destination in the bottom bar.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Written in C# rather than as a XAML control because it is a button with a label and a rule about the
|
||||
/// accent bar, and a templated control for that would be more machinery than the thing it draws.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>The accent bar is on top, not underneath.</b> The design puts it there in the nav rail and it is
|
||||
/// worth keeping on a phone for a reason the desktop does not have: the bottom of a phone screen is under
|
||||
/// the system's own gesture bar, and an indicator drawn there is one the user cannot see.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
internal sealed class NavButton : Button
|
||||
{
|
||||
public static readonly StyledProperty<string> LabelProperty =
|
||||
AvaloniaProperty.Register<NavButton, string>(nameof(Label), string.Empty);
|
||||
|
||||
public static readonly StyledProperty<bool> IsCurrentProperty =
|
||||
AvaloniaProperty.Register<NavButton, bool>(nameof(IsCurrent));
|
||||
|
||||
private readonly Border indicator;
|
||||
private readonly TextBlock text;
|
||||
|
||||
public NavButton()
|
||||
{
|
||||
Background = Brushes.Transparent;
|
||||
BorderThickness = new Thickness(0);
|
||||
CornerRadius = new CornerRadius(0);
|
||||
Padding = new Thickness(0);
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch;
|
||||
VerticalAlignment = VerticalAlignment.Stretch;
|
||||
HorizontalContentAlignment = HorizontalAlignment.Stretch;
|
||||
VerticalContentAlignment = VerticalAlignment.Stretch;
|
||||
|
||||
indicator = new Border
|
||||
{
|
||||
Height = 2,
|
||||
VerticalAlignment = VerticalAlignment.Top,
|
||||
Background = Brushes.Transparent,
|
||||
};
|
||||
|
||||
text = new TextBlock
|
||||
{
|
||||
FontSize = 9,
|
||||
FontWeight = FontWeight.SemiBold,
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Foreground = new SolidColorBrush(Color.Parse("#566059")),
|
||||
};
|
||||
|
||||
Content = new Panel { Children = { indicator, text } };
|
||||
}
|
||||
|
||||
public string Label
|
||||
{
|
||||
get => GetValue(LabelProperty);
|
||||
set => SetValue(LabelProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>Whether this is the destination currently showing.</summary>
|
||||
public bool IsCurrent
|
||||
{
|
||||
get => GetValue(IsCurrentProperty);
|
||||
set => SetValue(IsCurrentProperty, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
|
||||
if (change.Property == LabelProperty)
|
||||
{
|
||||
text.Text = Label;
|
||||
}
|
||||
else if (change.Property == IsCurrentProperty)
|
||||
{
|
||||
// Both the label and the bar, not just the bar. Colour alone at 9pt on a dark background is a
|
||||
// weak signal, and it is the only one a user with a colour deficiency would have.
|
||||
text.Foreground = new SolidColorBrush(Color.Parse(IsCurrent ? "#3CE88F" : "#566059"));
|
||||
indicator.Background = IsCurrent
|
||||
? new SolidColorBrush(Color.Parse("#3CE88F"))
|
||||
: Brushes.Transparent;
|
||||
}
|
||||
else if (change.Property == FontFamilyProperty)
|
||||
{
|
||||
text.FontFamily = FontFamily;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
|
||||
// Resolved here rather than in the constructor: application resources are not reachable while the
|
||||
// control is still being constructed, and a hard-coded stack would drift from Palette.axaml.
|
||||
if (Application.Current?.FindResource("MonoFont") is FontFamily mono)
|
||||
{
|
||||
text.FontFamily = mono;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user