Public Access
Give DodoSSH a phone, and a shared shell for both heads to drive
The Android head from docs/android-port.md, taken as far as its step 6. Step 3, the spike, is answered and its throwaway screen is gone: libsodium.so and libe_sqlite3.so are both in the arm64 APK, so NSec resolves its native half on Android despite shipping no Android build, and the local cache opens. Two findings the audit could not have had: Avalonia.Controls.WebView only ships net10.0-android36.0, which settles the open "which Android versions" question at targetSdk 36; and Android has blocked cleartext HTTP since API 28, so the terminal renderer needs a network security config scoped to 127.0.0.1 or the WebView loads nothing. DodoSSH.Client.Shell is new and is why the phone can exist: the view models, the terminal renderer files and the palette moved there so both heads drive one state machine and draw from one set of tokens. The desktop head is otherwise untouched and its 144 tests still pass. The platform pieces behind interfaces that already existed: the profile directory from filesDir, a device key wrapped by a StrongBox-backed key that a fingerprint releases, and a foreground service so a shell outliving a vault lock stays true on a platform that stops backgrounded processes. Sign-in is deliberately absent rather than approximated. It needs an app link, because reusing the desktop loopback listener is the attack RFC 8252 section 8.3 names.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
// The Android SDK's own namespaces are reached through `global::` throughout this project, and it is not
|
||||
// a style choice. This assembly's root namespace ends in `Android`, so inside it a bare `Android.App`
|
||||
// binds to `DodoSSH.Client.Android.App` — this head's own Avalonia application type — rather than to the
|
||||
// platform. The desktop head hit the same class of collision and answered it by renaming its type; here
|
||||
// the collision is in the namespace itself, so the qualification is the honest fix.
|
||||
using global::Android.App;
|
||||
using global::Android.Content.PM;
|
||||
|
||||
using Avalonia.Android;
|
||||
|
||||
namespace DodoSSH.Client.Android;
|
||||
|
||||
/// <summary>
|
||||
/// The launcher activity.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Deliberately empty. Avalonia is configured on the application object — see
|
||||
/// <see cref="DodoSshAndroidApplication"/> — and the desktop head's <c>Program.Main</c> has no counterpart
|
||||
/// at all here: Android constructs the activity, and its <c>[STAThread]</c> is a WebView2 requirement that
|
||||
/// means nothing on this platform.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>The ConfigurationChanges list is load-bearing.</b> Without it Android destroys and recreates the
|
||||
/// activity on every rotation and every time the software keyboard appears — and this head holds live SSH
|
||||
/// sessions and a terminal data plane behind a process-wide composition root. Letting the activity restart
|
||||
/// would tear the Avalonia application down under them. Declaring the changes handled is what keeps a
|
||||
/// shell alive across turning the phone sideways, which is the same promise the foreground service makes
|
||||
/// about backgrounding, arrived at from a different direction.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <c>SingleTask</c> for the sign-in redirect: the authorization response comes back as an intent, and any
|
||||
/// 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>
|
||||
/// </remarks>
|
||||
[Activity(
|
||||
Label = "DodoSSH",
|
||||
Theme = "@style/DodoTheme",
|
||||
MainLauncher = true,
|
||||
LaunchMode = LaunchMode.SingleTask,
|
||||
ConfigurationChanges = ConfigChanges.Orientation
|
||||
| ConfigChanges.ScreenSize
|
||||
| ConfigChanges.ScreenLayout
|
||||
| ConfigChanges.SmallestScreenSize
|
||||
| ConfigChanges.KeyboardHidden
|
||||
| ConfigChanges.UiMode)]
|
||||
public sealed class MainActivity : AvaloniaMainActivity
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user