Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
6d9fdee
Add SFTP, fix backup and restore, and theme the terminal for v2.1.0
GlobalTechInfo Oct 8, 2026
029c776
Merge branch 'main' into dev
GlobalTechInfo Oct 8, 2026
2dbef66
Add findings.md: own rootfs images research and roadmap
Oct 8, 2026
19bc5ca
findings.md: Phase 2 results and what the boot harness cannot test
Oct 8, 2026
7e743f7
Add Gentoo, and consume the published rootfs index
Oct 9, 2026
ca0c68e
findings.md: phases 3-5, the expanded distro list, and the build bugs
Oct 9, 2026
8791ed9
findings.md: twelve recipes, nine images, and the tar flag-cluster bug
Oct 9, 2026
8147e10
findings.md: NixOS removed, because the source is not a rootfs
Oct 9, 2026
e42f0cc
findings.md: OpenKylin investigated and not built; pacman space error…
Oct 9, 2026
8a71326
findings.md: the definitive list of twenty, with sources and what is …
Oct 9, 2026
f0dab0b
findings.md: proot-distro v5 installs from OCI references and no long…
Oct 9, 2026
3ee6be5
findings.md: termux-docker is not an installable rootfs
Oct 9, 2026
1269dc6
findings.md: nixos/nix is a nix installer base, /bin/sh dangles into …
Oct 9, 2026
e3409c9
findings.md: four package-manager installations, and the one-line tes…
Oct 9, 2026
e18b550
findings.md: the pacman error was a mountinfo device mismatch, not di…
Oct 10, 2026
5d33ac7
findings.md: final state, 59 images across 19 recipes
Oct 10, 2026
076a638
openSUSE: recreate the zypp cache tree; stop calling a failed downloa…
GlobalTechInfo Oct 13, 2026
228ca61
Assert that openSUSE creates its zypp cache tree, not only that it ne…
GlobalTechInfo Oct 13, 2026
769cd44
Clear a stale pacman lock the same way apt and dpkg already are
GlobalTechInfo Oct 13, 2026
2660497
openSUSE: remove the libzypp service, which is what restores the colo…
GlobalTechInfo Oct 13, 2026
f7edf93
Delete the systemd hooks on every start; HookDir does not disable them
GlobalTechInfo Oct 13, 2026
1bab6a5
Revert the manjaro change; add mkinitcpio hooks and fix install-time …
GlobalTechInfo Oct 13, 2026
c6470c3
pacmanXfer: stop deleting the XferCommand line it just set
GlobalTechInfo Oct 13, 2026
d457d36
Withdraw the app-side hook deletion; it caused warnings and never held
GlobalTechInfo Oct 13, 2026
731feea
Install a rootfs with the modes the archive recorded
GlobalTechInfo Oct 14, 2026
8bd08f4
Document v2.2.0: changelog, README distro tables, and the build host
GlobalTechInfo Oct 14, 2026
f12d3af
openSUSE: create the per-repository cache directories at session start
GlobalTechInfo Oct 15, 2026
7280d0d
Registry: refresh the built-in image hashes
GlobalTechInfo Oct 15, 2026
e8f5850
Report the file modes extraction could not apply
GlobalTechInfo Oct 15, 2026
89f0d89
openSUSE: take the colon out of the repository alias
GlobalTechInfo Oct 15, 2026
83bcd00
openSUSE: do the repository fix from Kotlin, not from sed
GlobalTechInfo Oct 15, 2026
baa62f1
openSUSE: remove a cache directory that holds no index
GlobalTechInfo Oct 15, 2026
705ceee
openSUSE: create the solv cache directory instead of deleting it
GlobalTechInfo Oct 15, 2026
a6b6dda
openSUSE: take the cache directory name from the service, not the header
GlobalTechInfo Oct 15, 2026
16e546f
openSUSE: repo_1_enabled=0 is not an alias
GlobalTechInfo Oct 15, 2026
6fecdeb
openSUSE: build the solv cache ourselves, because zypper cannot be tr…
GlobalTechInfo Oct 15, 2026
e3012e0
openSUSE: always refresh before building the cache, and never block o…
GlobalTechInfo Oct 15, 2026
00d234a
openSUSE: build the package cache into the image, not into the session
GlobalTechInfo Oct 15, 2026
faa34ad
Verify downloads against the release index, not against this APK
GlobalTechInfo Oct 15, 2026
b57e2ef
Refresh the built-in image checksums from the release
GlobalTechInfo Oct 15, 2026
820149c
Ask the release what it is holding, instead of trusting a published i…
GlobalTechInfo Oct 16, 2026
04273c1
Report the whole cause chain when an install fails
GlobalTechInfo Oct 16, 2026
658b001
openSUSE: stop renaming the repositories out from under the cache
GlobalTechInfo Oct 16, 2026
aacf182
Say whether the shipped index reached the device
GlobalTechInfo Oct 16, 2026
fec0fff
Drop --link2symlink: it corrupts the package cache and emulates nothing
GlobalTechInfo Oct 16, 2026
054c982
Revert "Drop --link2symlink: it corrupts the package cache and emulat…
GlobalTechInfo Oct 16, 2026
3e242a4
Put --link2symlink back, and pin why it has to be there
GlobalTechInfo Oct 16, 2026
533c58a
Pin --link2symlink on the exec line, not anywhere in the file
GlobalTechInfo Oct 16, 2026
f3cb643
Open the shipped index the way libzypp does, and report what comes back
GlobalTechInfo Oct 16, 2026
6a4a798
openSUSE: stop renaming the repositories, and rebuild the index in th…
GlobalTechInfo Oct 16, 2026
a66c419
Report what the gcc-libs repair found, instead of discarding it
GlobalTechInfo Oct 16, 2026
ff12d1f
Install: clear the old rootfs properly, and stop discarding hard links
GlobalTechInfo Oct 16, 2026
82bd195
Install: correct the record on hard links, and fix the removal order
GlobalTechInfo Oct 16, 2026
427a82a
Restore: a hard-linked copy keeps its mode, and both link forms resolve
GlobalTechInfo Oct 16, 2026
2eef89f
Setup: ask the gcc-libs question that is actually being asked
GlobalTechInfo Oct 16, 2026
6d38a96
Setup: remove the gcc-libs repair instead of quieting it
GlobalTechInfo Oct 16, 2026
8076636
Distro: /root is 0550 on four images, so nothing written into it ever…
GlobalTechInfo Oct 16, 2026
e3f0ecf
CHANGELOG: record the v2.2.0 fixes made after the images were published
GlobalTechInfo Oct 16, 2026
abc7241
SSH: open a server's sessions over mosh, and say which one you got
GlobalTechInfo Oct 19, 2026
7832157
UI: three themes were drawing the wrong colour, and none of it was in…
GlobalTechInfo Oct 19, 2026
0e79d78
Merge main into dev: v2.3.0 arrives with v2.2.0 still in history
GlobalTechInfo Oct 19, 2026
a32a3c4
Tests: stop one test class reading another class's side effects
GlobalTechInfo Oct 19, 2026
c7152aa
Release: bump versionCode to 6
GlobalTechInfo Oct 19, 2026
da3f110
Font import: the picker emptied every folder it was asked to filter
GlobalTechInfo Oct 19, 2026
af23f0d
Mosh: five review findings, each verified before it was fixed
GlobalTechInfo Oct 19, 2026
4ae486d
Connecting overlay: shown for plain ssh sessions and the SFTP browser…
GlobalTechInfo Oct 19, 2026
728172f
Passphrase prompts: asked when they can help, never silenced by an ol…
GlobalTechInfo Oct 19, 2026
766bab0
ssh overlay waits for the session's first output
GlobalTechInfo Oct 20, 2026
5037059
the connection test's result card is copyable
GlobalTechInfo Oct 20, 2026
ef97719
the test card copies, succeeds quietly, and stops naming the build ma…
GlobalTechInfo Oct 20, 2026
728021a
revert build-openssh.sh to its v2.2.0 state (30941e7)
GlobalTechInfo Oct 20, 2026
69241cd
review fixes: per-attempt cancellation, unlocked browser check, the p…
GlobalTechInfo Oct 20, 2026
feb7190
Fix mosh no-reply close, credential-loop stop, native error copying
GlobalTechInfo Oct 20, 2026
5094371
Fix mosh session left at logout when the remote shell exits
GlobalTechInfo Oct 20, 2026
1a2d08f
Clear the six compiler warnings at the source, and gate the build on …
GlobalTechInfo Oct 20, 2026
ddaffd7
Honour the failed mosh session's press-Enter promise
GlobalTechInfo Oct 20, 2026
bf876ae
Record the session the button sits on
GlobalTechInfo Oct 21, 2026
38858d1
Give the recording player a renderer and the app's palette
GlobalTechInfo Oct 21, 2026
26665f1
Answer the player view's client before its first layout
GlobalTechInfo Oct 22, 2026
ee3f6ca
Replay the recording into the parser, not through a pty
GlobalTechInfo Oct 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
775 changes: 761 additions & 14 deletions AGENTS.md

Large diffs are not rendered by default.

197 changes: 197 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,203 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v2.3.0]

### Added

- **mosh, as a per-server option.** A saved server can be set to open over mosh instead of
SSH. mosh keeps no connection at all once the shell is up — it runs over UDP — so a
session survives switching between WiFi and mobile data, a lift, or a cell handover, all
of which drop a long-lived TCP connection. The protocol is mosh's own, built from mosh
1.4.0's libraries and shipped as a per-ABI native client for all four architectures, the
same way the bundled `ssh` binary already ships.
- **The transport is visible.** The session title and the notification show the transport
that actually connected, not the one that was requested, so a server whose card says
*mosh* next to a session saying *· ssh* has made the fallback visible without any extra
state. See Fixed.
- **Switching transport from a running session.** **⋮ → Connection → Reconnect using mosh**
or *Reconnect using SSH*, on SSH sessions. It opens a new session over the other protocol
and closes the old one — the two are separate processes, so one cannot be converted into
the other — and the server's saved preference moves with it.
- **A connecting indicator** while an SSH or mosh session is brought up, covering the
plain-SSH connect and the SFTP browser's first connection as well, each with a way
out if the connection stalls.

### Fixed

- **Recording a session actually records that session.** Recordings used to mean
"arm it now, and the *next* session you open gets captured" — which often
happened to be a throwaway, produced a file whose name was guessed at start-up,
and said nothing when no recording ever recovered the terminal output. Now the
Record button captures the session it sits on: the emulator's visible screen is
snapshotted on every drawn frame and re-encoded as plain ANSI (positioned rows
and SGR state deltas, the same playback the Recordings player already speaks),
so exactly what the session drew is what replays. Files are written under the
app's recordings directory in one place, named at start from the session's own
identity (distro or server), flushed per frame, and cleaned up when a recording
ends empty — ending with its session, by hand, or when the activity dies. Both
ends log through AppLog: started, stopped (with frame and byte counts), and why
a storage failure or a frame write stopped it.
- **Playing a recording no longer crashes the app.** The replay view attached its
session and never gave it a font, so the first layout ran straight into
TerminalView's null renderer — the same crash the mosh bootstrap hit, from the
same missing order. The player now sets the size and typeface before the
attach, and applies the theme's palette after it, so a recording replays in
the same colours it was made with. Font and palette helpers moved to a shared
`TerminalTheming` so every surface showing a terminal uses the same ones. The
view's client pointer is the next unguarded slot in the same row of machinery
(`updateSize` calls its `onEmulatorSet` with no guard), so the player answers
that too — and pinch zooms the replay the way the session terminal does.
- **And replay no longer runs a shell at all.** Feeding the file through a pty
made playback depend on whichever `stty` vocabulary the device ships: on this
one the child exits `stty: -icanon: unknown option`, code 1, before a single
frame is fed in — the replay was a blank page that finished before it started.
The pty existed only because `TerminalView` needs a session; the emulator does
not. Playback now feeds chunks of the file into a private `TerminalEmulator`
on a background thread and draws it with the library's own renderer — no
process, no stty, nothing device-dependent in the middle.
- **"Press Enter to close this session and open one over SSH" now works.** The
failed mosh session's report is printed by a process that then exits, so nobody
was listening for the Enter it asked for: dismissing the failure dialog left a
dead terminal eating keys. The dialog's dismissal now arms the promise — and an
Enter aimed at that dead session is intercepted at the terminal client, where
the library offers both key events and input-method-committed text before it
types anything — closed, and the same server opens over SSH, without changing
the mosh preference the user chose to keep.
- **The build is back at zero warnings.** Six compiler warnings from Android 16/17's
deprecation wave are fixed at the source rather than suppressed: the deprecated
`TRIM_MEMORY_RUNNING_LOW` threshold (any trim signal now drops a cache that is a
map clear to rebuild), the deprecated `announceForAccessibility` (the connecting
overlay's message is now an accessibility live region whose text changes on screen,
the sanctioned replacement), the deprecated `SHOW_IMPLICIT` flag (deprecated and
without effect since Android 16; the flag is simply omitted), the Int `versionCode`
field (`PackageInfoCompat.getLongVersionCode`), and two conditions that compared
things they could never satisfy — a `File` tested for null and a non-null list's
element tested for null.
- **`exit` at the remote shell now ends a mosh session.** mosh-server says goodbye
on its own protocol — encrypted packets that arrive wherever the session's
packets do, ICMP-filtering NAT and all — and the client never acted on it: by
the time the shell exits every state is acknowledged, so nothing more is sent,
the network falls silent in both directions, an error-based detector never
hears an error, and the session sat at `logout` for ever. Measured before the
fix, against a real mosh-server: killed after 50 s, still running. After it:
a clean goodbye, exit 0, no failure text, in a few seconds.
- **A mosh session that never reached the server is covered until it says so.** The
connecting overlay used to end the moment the bootstrap handed back a listening
server, on the theory that the client draws its own connecting status — which it
no longer does. A blocked UDP path then read as a blank terminal for the full
twenty-second wait. The overlay now ends on the session's first drawn output, the
failure report or the 30-second cap, exactly as a plain ssh connect is handled.
- **Closing a mosh session that never connected can no longer leave the screen
behind it stale.** The no-reply dialog's early return skipped the session
bookkeeping the ordinary close runs — the split teardown, the re-attach to the
surviving session, the title, the drawer and the home-screen widget — so drawer
rows could point at sessions that no longer existed. Only the activity's own
close is skipped while the dialog is up; everything else runs.
- **The no-reply dialog no longer claims a session was opened.** Cancel opens
nothing, and the buttons open one on choice; the message now says the server can
be opened over SSH instead.
- **The credential loop stops on a failure no credential can fix.** An attempt
that is not a refusal — the host unreachable, or it accepted a key and then had
no mosh-server to run — used to send the remaining keys and the password stage
at the same question, each a fresh connection and possibly a prompt on a server
that had already shown its hand. The first such attempt now ends the loop and
carries its own reason out, instead of the last credential refusal masking it.
- **The native mosh client's error text can no longer be read while it is being
rewritten.** `mosh_session_last_error` returned a pointer into a string the
worker keeps reassigning, which a caller formatting the previous failure while
the next `recv()` threw could read mid-mutation — the remote-exit path does
exactly that. The text is now copied out under the lock. The client's
`connected` flag is atomic (the caller's thread reads it while the worker
writes) and its consecutive-error counter is initialised rather than left
indeterminate.
- **A font collection is no longer accepted and then hidden.** `.ttc`/`.otc`
passed the import filter but neither the settings list nor the terminal's font
loader honours them, so an imported collection appeared in no list and could not
be removed. They are refused at the picker, where the refusal is readable.
- **Cancelling one connect attempt can no longer spoil another.** The attempts
shared one abandoned-flag, so a stalled attempt the user cancelled went on to
attach when a newer attempt had already begun — and could discard the session
they had just asked for. Results are now tracked per attempt: Cancel invalidates
only the attempt it was shown for, and a worker attaches only when its token is
still the newest.
- **The browser's SFTP connection check can no longer freeze the UI.** Asking
whether a connection is up took the same lock a running transfer holds for its
whole duration, so a tap that started a load waited behind it. The check now
takes no lock.
- **A passphrase key further down the list gets its prompt.** An open key's
refusal — where no dialog can help — was spending the connection's one
opportunity to ask, so a server that accepted the *second* key, an encrypted
one, rejected the connection without ever asking for its phrase. Only a
question actually asked now counts as asked.
- **Cancelling during the attach-wait is not a session failure.** It ended the
session the overlay was attached to, which arrived as a kill and was then
reported to the user as "session ended" with status -9 — a dialog for the
cancel they had just chosen.
- **The connection test's result can actually be copied now.** Long-press selection
was broken by a movement-method conflict and is fixed; a Copy button has been
added to the card as well, which puts the whole result on the clipboard in one
tap without depending on selection working at all.
- **A successful connection test no longer prints a wall of debug trace.** The
`-v` output that diagnoses a failure is noise on a success; the success card is
now what the connection used and what answered — the method, the key's label
where one was used, and the server's own version — and the failure card keeps
the trace, with lines merely naming the build machine's directories filtered
out of it.
- **The passphrase hint on the test card is shown when it is true.** It used to
appear on a success, saying the check "can only pass for a key without one" —
after a check that had just passed with a passphrase key. It now appears on a
refusal, and says both things a refusal can mean: a passphrase that never
arrived, or a key the server has no record of.
- **The SSH connecting indicator now covers the actual connection.** It used to end
when the session process started, which is the fastest part: TCP, key exchange and
authentication happen afterwards, on a blank terminal. The indicator now stays
until the session draws its first output — the banner, the prompt, or ssh's own
passphrase question — with a 30-second cap so a connect that produces nothing
cannot trap the user behind it. Cancelling while it is up also stops the session
it was attached to, including during that wait.
- **A key made with a passphrase now works everywhere, and asking for it stopped
going wrong.** The connection test and file transfer failed on such keys without
ever offering the prompt, for two reasons: a passphrase question dismissed once
was remembered in process-wide state and every later connection kept failing
without asking again, and a phrase held in memory was treated as proof that no
question could help — so one mistyped passphrase silenced asking for the rest of
the app's life. Both are gone: a dismissed prompt belongs only to the connection
attempt it interrupted, and on refusal the key file itself is probed locally —
an open key can never make a passphrase work, so it is never asked about; an
encrypted key is offered its dialog on every connection attempt.
- **Font import works on real devices.** The document picker filtered with `font/*`, but
providers report the same font under many names — `application/x-font-ttf`,
`application/font-sfnt`, or plain `application/octet-stream` for anything a browser
downloaded — so every folder containing fonts opened empty and nothing could ever be
selected. The picker now opens unfiltered, and what is picked is validated instead by the
display name the provider records; a non-font file is named in a message rather than
imported as one. `ttc`/`otc` collections are accepted where the old import would have
rewritten them to `.ttf`.
- **A failed mosh session no longer leaves an empty terminal.** It opens over SSH instead and
says why, offering either to keep asking for mosh next time or to stop asking for this
server. Nothing about it is silent: a fallback with no explanation is indistinguishable
from the preference having been ignored.
- **`exit` at the remote shell ends the session.** `mosh-server` closes its UDP port, the
next packet draws ICMP port-unreachable and every receive fails; the client counted those
and now treats several in a row with no successful receive as the far end saying the
session is over. Previously the terminal printed `logout` and then sat there for ever.
- **A live but quiet session is no longer at risk.** The earlier fix for the above removed an
idle timeout, which had the opposite failure — a mosh session is silent most of the time
somebody is reading a man page, and killing it for being quiet would break the one thing
mosh exists for. There is now one timer, and it is about waiting for the first packet.

### Known limits

- **mosh needs UDP 60000–61005 open to the server, all the way through**, which means the
cloud provider's security list *and* the host's own firewall. Cloud images commonly ship a
ruleset allowing TCP 22 and rejecting everything else, and opening the port in the
provider's console alone is not enough.
- **A phone behind carrier NAT may not be able to receive the UDP at all.** SSH works in
both cases; only mosh needs the return path. mosh over an overlay network such as Tailscale
usually works regardless, because the traffic travels inside a connection the phone
already opened.

## [v2.2.0]

### Highlights
Expand Down
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ install from either and update in place without reinstalling.
- Multiple sessions per distro, and a separate session for every distro
- One-tap package updates, storage usage, SSH servers, ANSI recordings and base image updates from the home screen
- Bundled OpenSSH client: saved servers connect straight away, with no distro required
- **mosh** as a per-server option — survives WiFi/mobile switches that drop SSH, falling back to SSH automatically when the server or your network cannot carry UDP
- **SFTP file browser** for saved servers — upload, download, search, permissions and symlinks
- Keyring for SSH: named keys, ed25519/ECDSA/RSA-4096, passphrases, import, and per-server binding
- Distro backup and restore that survives uninstalling the app
Expand Down Expand Up @@ -757,6 +758,68 @@ passphrase.
which is what you do when a server is rebuilt or moved and its key legitimately changes.
**Import config** reads an OpenSSH `config` including its `Host` blocks.

#### mosh

**mosh** is a different way to reach the same shell. SSH is one long TCP connection, and a
phone changes networks constantly — switching between WiFi and mobile data, or a lift, or a
cell handover — which drops it. mosh keeps no connection at all: it runs your shell over UDP
and the phone's address is allowed to change underneath it. That is the entire point, and it
is why it survives a network switch where SSH would need reconnecting.

The protocol is mosh's own, implemented here from mosh 1.4.0's libraries and shipped as a
per-ABI native client — the same way the bundled `ssh` binary ships. Nothing to install on
the device.

**Turning it on** is one checkbox on a saved server: **Options → Use mosh instead of SSH,
when it is available**. It is a preference and never a requirement — see *When it cannot be
used* below.

**What the server needs.** Two things, and both are outside the app:

1. `mosh-server` installed on the host (`apt install mosh-server` and it is done).
2. **UDP 60000–61005 open to your device — all the way through.** That means the cloud
provider's security list *and* the host's own firewall. Cloud images commonly ship a
ruleset that allows TCP 22 and rejects everything else, and opening the port in the
provider's console alone is not enough.

**When it cannot be used, you get SSH, and you are told why.** If `mosh-server` is missing,
the port is blocked, or your own network will not carry UDP, the session opens over SSH
instead and says so. It never leaves you with an empty terminal.

**Knowing which one you have.** The session title and the notification both say the transport
in use — `Test · mosh` or `Test · ssh`. That is the transport that actually connected, not
the one that was asked for, which is what makes the interesting case visible: a server card
that says *mosh* next to a session that says *· ssh* has told you the fallback happened,
without any extra state.

**Switching from a running session.** **⋮ → Connection → Reconnect using mosh** (or *using
SSH*), available on SSH sessions. It opens a new session over the other protocol and closes
the old one — the two are separate processes, so one cannot be converted into the other. The
server's saved preference moves with it, so the next tap reverses it.

**Over Tailscale and other overlays, mosh usually just works.** The traffic travels inside a
connection the phone already opened, so a server's own firewall never sees it. Over a public
IP it has to be open, and on a phone behind carrier NAT it may not be — see below.

**If a session hangs and then reports that nothing arrived,** the client never received a
single UDP packet back from the server. Which direction dropped is not something the client
can see — the outbound packet may have arrived and the reply been lost on the way back — so
check both:

- UDP 60000–61005 is not open **to** the server, so the outbound packet never arrived, or
- the path back **from** the server is blocked — the cloud provider's firewalls are
separate inbound and outbound, and a reply to a UDP flow the phone opened can be dropped
by carrier NAT on mobile data.

SSH works in either case; only mosh needs the UDP. To check the host side:

```sh
sudo iptables -L INPUT -n --line-numbers | grep -E '60000|REJECT'
```

A catch-all `REJECT` with no UDP rule above it is the usual finding. Oracle and other cloud
images ship one.

#### SFTP

**SFTP** is a home-screen tool: **Tools → 🔑 SSH → Browse files**. It is not a terminal command, and
Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ android {
applicationId = "com.redtermapp"
minSdk = 24
targetSdk = 37
versionCode = 5
versionName = "2.2.0"
versionCode = 6
versionName = "2.3.0"
}

signingConfigs {
Expand Down
Binary file added app/src/main/assets/mosh/aarch64/redterm-mosh
Binary file not shown.
Binary file added app/src/main/assets/mosh/armv7a/redterm-mosh
Binary file not shown.
Binary file added app/src/main/assets/mosh/i686/redterm-mosh
Binary file not shown.
Binary file added app/src/main/assets/mosh/x86_64/redterm-mosh
Binary file not shown.
Loading
Loading