Files
DodoSSH/src/DodoSSH.Client.Android/Views/LockedScreen.axaml
T
jaap-jan 416f233657
ci / build and test (push) Successful in 1m13s
ci / android head (push) Failing after 5s
ci / api image (push) Successful in 23s
Let the lock screen wear the theme instead of its own copy of it
Four controls on this screen carried a full set of metrics each, and three of
them were a shared class rewritten by hand — UNLOCK is Button.primary down to
the font weight, the fingerprint button is Button.secondary, the passphrase box
is TextBox.field. Copies drift, and every difference between these and the
originals was drift rather than intent: corner radii of 6 from the first design
where v2's ladder says 9 for a button and 11 for a field, a label half a point
small, and the missing content alignment that put UNLOCK's word against the top
of its box and started this.

Two things fall out that are worth naming, because neither was the reported
fault. UNLOCK now has the pressed and disabled states the class carries, and
before it had neither — pressing it ran Fluent's default, which replaces the
accent fill with a theme grey, so the one button this application most wants to
look deliberate flashed the wrong colour under a thumb. And the passphrase box
is 44 rather than 48, which is what every other box on the phone is; the four
pixels bought it nothing that the button under it does not already give the
thumb.

RESET keeps two properties of its own against Button.danger, and they are the
two that carry meaning. That class is a DELETE beside a KEEP, where the red
outline says which of two offered actions costs something. Here there is no
pair, and a bordered red button under UNLOCK reads as the next thing to try
rather than as the last resort the screen means it to be.

The live-sessions disclosure is not a class anywhere — warn cards are plain
borders on all three screens that draw one — but its radius was 6 against the
12 the other two use, so it joins the ladder without joining a class.
2026-08-03 14:08:30 +02:00

135 lines
7.6 KiB
XML

<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:DodoSSH.Client.Shell.ViewModels"
x:Class="DodoSSH.Client.Android.Views.LockedScreen"
x:DataType="vm:MainWindowViewModel"
Background="{StaticResource Canvas}">
<!--
Design 01 — LOCKED, and one of the five states docs/android-port.md marks as most likely to be lost at
360dp. Two things on it are load-bearing and neither is decoration:
the count of shells still connected, and the paragraph under it. Locking describes the keychain and not
this phone's access to the hosts, and the desktop's README says so at length; on a phone, where the
lock screen is most of what a user sees, saying it here is the only place it fits. The block is absent
rather than empty when nothing is connected — a card reading "0 shells" would be noise on every launch.
Everything is one column with generous vertical slack above and below the controls, because the
software keyboard takes roughly half the screen the moment the passphrase box is focused. The slack is
what it eats.
-->
<ScrollViewer VerticalScrollBarVisibility="Auto">
<Grid RowDefinitions="*,Auto,Auto" Margin="24,0">
<!-- Identity. The chip names the account so a phone with two profiles is not a guess. -->
<StackPanel Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="10" Margin="0,48,0,36">
<Border Width="44" Height="44" BorderBrush="{StaticResource Accent}" BorderThickness="1"
HorizontalAlignment="Center">
<TextBlock Text="&gt;_" Foreground="{StaticResource Accent}" FontFamily="{StaticResource MonoFont}"
FontSize="16" FontWeight="SemiBold"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<TextBlock Text="DodoSSH" Foreground="{StaticResource Text}" FontFamily="{StaticResource MonoFont}"
FontSize="15" FontWeight="SemiBold" HorizontalAlignment="Center" />
<Border BorderBrush="{StaticResource BorderMid}" BorderThickness="1" CornerRadius="3"
Padding="9,3" HorizontalAlignment="Center"
IsVisible="{Binding AccountName, Converter={x:Static ObjectConverters.IsNotNull}}">
<TextBlock Text="{Binding AccountName}" Foreground="{StaticResource TextDim}"
FontFamily="{StaticResource MonoFont}" FontSize="10" FontWeight="Medium" />
</Border>
</StackPanel>
<StackPanel Grid.Row="1">
<!--
Enter unlocks, which matters more here than on the desktop: the software keyboard's action key is
the nearest thing to hand, and reaching past it to a button is the sort of friction that gets a
phone client called slow.
-->
<TextBox Classes="field secret" Text="{Binding Passphrase}" PlaceholderText="vault passphrase"
IsEnabled="{Binding !IsBusy}">
<TextBox.KeyBindings>
<KeyBinding Gesture="Enter" Command="{Binding UnlockCommand}" />
</TextBox.KeyBindings>
</TextBox>
<Button Classes="primary" Content="UNLOCK" Margin="0,10,0,0"
Command="{Binding UnlockCommand}" IsEnabled="{Binding !IsBusy}" />
<!--
Present only when this phone actually holds a device key. The design draws it unconditionally,
but offering a fingerprint that cannot open anything is worse than not offering one — see
AndroidDeviceKeyStore for the three ordinary ways it stops being available.
The label sets no font of its own: inside a Button.secondary it inherits the class's, which is
what stops this one reading half a point smaller than every other outlined button on the phone.
-->
<Button Classes="secondary" Margin="0,8,0,0"
Command="{Binding UnlockWithDeviceCommand}" IsVisible="{Binding CanUnlockWithDevice}"
IsEnabled="{Binding !IsBusy}">
<StackPanel Orientation="Horizontal" Spacing="9">
<Ellipse Width="16" Height="16" Stroke="{StaticResource Accent}" StrokeThickness="1.5"
VerticalAlignment="Center" />
<TextBlock Text="UNLOCK WITH FINGERPRINT" VerticalAlignment="Center" />
</StackPanel>
</Button>
<TextBlock Text="works with no network — the keychain decrypts on this phone"
Foreground="{StaticResource TextFaint}" FontFamily="{StaticResource MonoFont}"
FontSize="10" TextAlignment="Center" TextWrapping="Wrap" Margin="0,14,0,0" />
<TextBlock Text="{Binding StatusMessage}" Foreground="{StaticResource TextDim}"
FontFamily="{StaticResource MonoFont}" FontSize="10" TextAlignment="Center"
TextWrapping="Wrap" Margin="0,8,0,0" />
<!--
◆ The disclosure. Absent when there is nothing to disclose; never a card reading zero.
Not Border.card, and neither are its two counterparts on HOSTS and FILES: that class is a panel,
and this is a warning, so the background and the border are the warn pair rather than the chrome
one. What it does take is the radius the v2 ladder gives anything card-sized, which is what the
other two already draw.
-->
<Border IsVisible="{Binding HasLiveSessions}" Margin="0,22,0,0"
Background="{StaticResource WarnWash}" BorderBrush="{StaticResource WarnSoft}"
BorderThickness="1" CornerRadius="12" Padding="14,12">
<StackPanel Spacing="6">
<StackPanel Orientation="Horizontal" Spacing="8">
<!-- Live, not Accent: this marks shells that are still running, which is a fact and not a
thing to press. It read as green until v2 turned the accent blue. -->
<Ellipse Width="6" Height="6" Fill="{StaticResource Live}" VerticalAlignment="Center" />
<TextBlock Text="{Binding LiveSessionSummary}" Foreground="{StaticResource Warn}"
FontFamily="{StaticResource MonoFont}" FontSize="10" FontWeight="SemiBold"
TextWrapping="Wrap" />
</StackPanel>
<TextBlock Foreground="{StaticResource WarnText}" FontFamily="{StaticResource MonoFont}"
FontSize="10" TextWrapping="Wrap"
Text="Locked describes the keychain — not this phone's access to the hosts. Open sessions stay alive behind this screen." />
</StackPanel>
</Border>
</StackPanel>
<!--
The only answer to a forgotten passphrase, and it is deliberately the last thing on the screen and
the only red one. Nothing can recover a passphrase; this empties the phone and starts again.
Two of Button.danger's properties are overridden rather than taken, and they are the two that would
change what this control says. Elsewhere that class is a DELETE sitting beside a KEEP, where a red
outline marks which of two offered actions costs something; here there is no pair, and a bordered
red button under UNLOCK would read as the second thing to try rather than as the last. Borderless
and Medium is what keeps it a way out rather than an offer. Everything else — the height, the
centring, the colour, the type — is the class's.
-->
<Button Grid.Row="2" Classes="danger" Margin="0,28,0,20"
BorderThickness="0" FontWeight="Medium"
Command="{Binding SignOutCommand}"
Content="RESET THIS PHONE — forgot passphrase" />
</Grid>
</ScrollViewer>
</UserControl>