Commit graph

27 commits

Author SHA1 Message Date
jima
6c1440e80e Security audit 2026-04-11: PEM/Bearer redaction, telnet warning, host key prefix
Fourth full security audit (prod-only). Fixed 5 findings, deferred 3 high-priority items.

- FileLogger redacts PEM private key blocks and Bearer tokens
- EditConnectionScreen shows persistent telnet cleartext warning card
- SubscriptionRepository.migrateFromProApk made internal + idempotent
- Host key fingerprints stored as <keyType>:<fingerprint> with backward-compat fallback
- MainActivity.onUrlTapped allowlists http/https/ftp schemes only

Also bundled from this session:
- SFTP back button no longer navigates up folders
- Vault local save requires strong password (12+ chars, mixed)
- SSHSession cancel-on-auth stops retry loop immediately
- Version bump to 0.0.38

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 01:02:00 +02:00
jima
7f4aa15830 Server-driven auth, 3-attempt retry, remember-on-success, legacy test lab doc
Auth flow rewritten to be fully server-driven: no pre-connect password
dialog. TCP + KEX + host key verification happen first; password prompt
only appears when the server actually asks via keyboard-interactive or
the new prompted-password fallback (for servers that only accept the
"password" SSH method). Up to 3 password attempts per connection,
matching OpenSSH's NumberOfPasswordPrompts default.

"Remember password" checkbox now functional: AuthPromptResult threads
the remember flag through the callback chain; SSHSession stashes the
typed password in pendingRememberPassword; TerminalService persists it
to CredentialStore only after session.connect() succeeds — wrong
passwords are never saved.

Removed dead pre-connect dialog code: PasswordDialog composable,
PasswordResult, TerminalDialogRequest.PasswordPrompt, and
passwordPromptHandler.

Added docs/LEGACY_TEST_LAB.md: self-contained 2100-line guide for
building a dedicated server with 56 historical/modern Unix systems
for terminal parser conformance testing (Docker, QEMU/KVM, SIMH,
polarhome, IBM PowerVS). Includes all Dockerfiles, compose.yml,
SIMH configs, systemd units, and helper scripts inline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 19:50:18 +02:00
jima
e243b8e1e5 VP2 rotation fix, NumBlok mini toggle, HW keyboard auto-hide, audit
ViewPager2 rotation phantom (Google Issue 175796502):
- KeyboardView.onSizeChanged rebuilds buildPages() on any width
  transition including initial 0→actual layout, not just on resize.
  The first buildPages() from setup() runs at width=0 (view still
  detached), VP2's internal RecyclerView can't reliably re-anchor to
  the real dimensions that arrive later, leaving a fractional scroll
  offset that leaks the neighbor page on the right edge. A second
  buildPages() once the real size is known produces a clean result.
- KeyboardView.lastModifierStates caches CTRL/ALT/SHIFT state so it
  survives the rebuild.
- Works around an unfixed VP2 1.1.0 bug for non-FragmentStateAdapter
  usage. Fresh launches and all rotation paths confirmed clean on S23.

NumBlok toggle on mini numpad:
- KeyDefinition gains numLabel/numAction optional fields
- KeyAction gains ToggleNumBlock data object
- LayoutParser understands "numLabel"/"numAction" JSON and the
  "toggle_numblock" action type
- KeyboardPageView.numBlockActive swaps label rendering; toggle key
  gets LOCKED-style amber glow while active
- TerminalKeyboard.numBlockActive state; handleKeyAction swaps
  action→numAction; attachMiniTo carries state across rotation rebuilds
- Mini last row: Num 0 \ (base) → Num Ins ~ (numblock)
- Rows 1-3 map to PC-keypad nav: Home/↑/PgUp, ←/Esc/→, End/↓/PgDn
- QuickBarCustomizer serializer/deserializer learned toggle_numblock

Hardware keyboard auto-hide:
- MainActivity derives hasHwKeyboard from LocalConfiguration.current.
  keyboard != KEYBOARD_NOKEYS && hardKeyboardHidden != HARDKEYBOARDHIDDEN_YES
- LaunchedEffect(hasHwKeyboard) { ckbHidden = hasHwKeyboard } seeds
  the toggle on every HW kb connect/disconnect
- In HW kb mode the kebab Show/Hide controls both CKB and QuickBar as
  a pair: val qbShownByUser = if (hasHwKeyboard) !ckbHidden else
  quickBarVisible
- Normal mode unchanged (QB follows its own quickBarVisible pref)

Number row dropdown:
- KeyboardSettingsDialog now shows all four options (top/left/right/
  hidden) in both orientations. Previously left/right were filtered
  out in portrait, preventing the user from setting the mini mode
  without first rotating into landscape.
- Portrait-override on the effective numberRowMode stays in place
  (mini is landscape-only by design — documented in CLAUDE.md and
  auto-memory).

Audit fixes:
- KeyboardPageView: removed key.hint!! by capturing into a local
  hintText val so smart-cast flows through the render branches
- SSHKeyLoader.loadEd25519FromPkcs8: require(seed.size == 32) so a
  malformed PKCS#8 blob fails fast with a clear message instead of
  crashing deep inside EdDSAPrivateKeySpec
- vault_crypto.cpp nativeEncrypt: secure_zero(ptPtr, ptLen) before
  ReleaseByteArrayElements on all three paths (success + two error
  paths), matching the existing key-handling pattern for plaintext
  defence-in-depth through the JNI boundary

Docs:
- TECHNICAL.md: NumBlok, VP2 rebuild workaround, HW keyboard auto-hide,
  portrait mini override notes
- CLAUDE.md: lib-terminal-keyboard module description updated with
  the same invariants
- TODO.md: recently-completed items through 2026-04-06
- Audit.md: investigation log including false positives for future
  reference

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:23:13 +02:00
jima
f41d9298f6 Full codebase audit: security fixes, bug fixes, dead code, Compose migration
Security: QR key bytes zeroed in vault export/import, Socks5 null-safety fix.
Bugs: SSH auth cascade no longer silently succeeds without callback, stderr
readLoop no longer causes premature disconnect, dimension overlay cols×rows,
cursorUp/Down respects scroll region boundaries.
Quality: runBlocking→StateFlow (27 calls), MaterialAlertDialogBuilder→Compose
dialog, SSHJ stderr capture race serialized, dead code cleanup across all
modules, string resources for hardcoded text, doc updates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 22:32:03 +02:00
jima
bb7662ca63 Audit: security hardening, bug fixes, dead code cleanup across all modules
vault-crypto: secure_zero for all sensitive buffers (Argon2 core, BLAKE2b,
GCM tag/lengths), JNI NewByteArray null checks, Kotlin input validation.
lib-ssh: SSHSession thread safety (debugLogBuffer sync), EOF check fix,
session reuse guard, stderr restoration in finally block.
lib-terminal-view: italic rendering (faux skew), hidden text (fg=bg),
DECCKM reset fix, reflowResize mode preservation, render thread TOCTOU fix.
lib-terminal-keyboard: dead code removal, kotlin.math migration, touch
allocation elimination in KeyboardPageView.
app: Hilt-injected CredentialStore (was duplicate instance), hardcoded
strings to resources (BiometricAuthManager, VaultImportViewModel),
null safety (!! elimination in 5 files), deprecated API fixes, unused
imports/strings cleanup, test API drift fixes, migration 10→11 test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 08:44:45 +02:00
jima
429ad179ec SFTP: ProxyJump, keepalive, disconnect UI, Connect to Terminal; fling fix, callback audit
SFTP standalone sessions now use shared buildJumpChain for ProxyJump support,
start keepalive monitor for zombie detection, and have full disconnect/reconnect
UI with SSH state monitoring. Tab overflow menu adds "Connect to Terminal".

Fling scroll removes hard distance cap (flingMaxRows) so momentum decays
naturally. Selection toolbar Google button wired. Mouse reporting gated by
pro feature. Dead code (onSaveSnippet) removed. Write logging for paste debug.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 00:39:03 +02:00
jima
2fd8308056 Mouse tap-only, ProxyJump probe fix, Ed25519 key loading, jump tunnel monitor
Mouse mode: only taps forwarded as clicks, scroll/select/fling always work.
Active pane zIndex ensures touch events reach correct session.
SSHKeyLoader: auto-detect key format, Ed25519 PKCS#8 via net.i2p.crypto.eddsa.
ProxyJump probe: tunneled connections trust isConnected (no raw socket).
Jump monitor: watches tunnel state, force-disconnects on tunnel death.
Copy Private Key works without biometric lock enabled.
KeyLoadingTest: 9 tests covering all key formats.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:55:29 +02:00
jima
34325f7119 Full codebase audit: crypto hardening, 6 critical bugs, i18n, dead code cleanup
Security:
- lib-vault-crypto: secure_zero (volatile memset ptr) replaces memset for all
  sensitive memory zeroing, null checks on JNI array access, key/password
  buffers zeroed before release, input validation on Kotlin API
- lib-ssh: keyboard-interactive auth no longer falls back to empty password

Critical bugs fixed:
- SFTP multi-select delete now handles all selected files (was only first)
- reconnectSession() handles Telnet and Local shell types (was always SSH)
- _activeSessions StateFlow updates now atomic via .update{}
- Telnet sessions now execute startup commands
- Clipboard queries cached on UI thread (render thread crash risk on some OEMs)
- TextAttr color mask fixed from 20-bit to 24-bit

Thread safety: @Volatile on SftpSession.sftpClient, SftpViewModel._allEntries;
synchronized debugLogBuffer; onViewRecycled prevents allPageViews leak

i18n: 37 new string resources (EN/ES/SV) for disconnect reasons, network
status, terminal banners, port forward errors, toolbar labels, toast messages

Dead code: removed boilerplate tests, tautological assertions, unused
StateFlows/functions, redundant night theme, unused string resources

Tests: fixed startupCommands mock (false→true), updated stale test names

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 10:00:26 +02:00
jima
5b07e2d686 SFTP: fix SSH disconnect, file type icons, multi-select, hidden files
Critical fix: SFTPClient.close() kills the parent SSH transport by
sending a channel EOF. Removed the close() call — just null out the
reference, SFTP subsystem cleans up when SSH connection closes.

Other SFTP fixes:
- Single SFTP session open (was opening twice — race corrupted transport)
- SftpSessionManager.open() is now suspend (no 500ms guesswork delay)
- SftpScreen always composed (preserves SAF launchers across picker)
- Keyboard/QuickBar hidden on SFTP tabs (tab type check)
- Disconnect notifications suppressed when app in foreground
- Title shows connection name (titleSmall style)

New SFTP features:
- File type icons (32dp): APK, image, video, audio, archive, PDF, code,
  shell, keys, text — color-coded per type
- Multi-select: tap icon to select, batch download/delete bar
- Hidden files hidden by default, toggle in overflow menu
- i18n strings for hidden files toggle (EN/ES/SV)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 02:53:25 +02:00
jima
678295b03b CKB hide/show, keyboard hints, QB fixes, nav fix, logcat in log export
- CKB: temporary hide via ckbHidden state (not pref change), tap terminal to reshow
- CKB/QB input now scrolls terminal to bottom (was IME-only)
- Keyboard hints: all keys get hints (removed label length filter), smart
  positioning (above by default, flips below if off-screen, clamped to edges)
- Long-press popup: auto-shrinks cells to fit screen width
- QB: hints on all keys (onKeyDown on ACTION_DOWN for all, not just repeatable),
  scroll cancels key repeat
- PICK_HOST Back returns to terminal when sessions are open
- Copy Log ZIP now includes logcat.txt (last 5000 lines)
- TerminalKeyboard.attachTo() safe for re-attachment
- SFTP: long-press context menu, settings detail expansion, i18n additions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 01:12:38 +02:00
jima
1d3b4b053c Full codebase audit: 7 critical bugs, DECSCNM rendering, UTF-8 chunk buffering, i18n
Critical bugs fixed:
- Missing MIGRATION_10_11 in TerminalService (crash on upgrade)
- jumpHostSessions was global (multi-session corruption) — moved to SessionEntry
- KeyboardSettings reset skipped every other view (forward-remove loop)
- DCS parser leaked backslash (ESC \ consumed 1 byte instead of 2)
- SOCKS5 clientThreads grew unbounded (memory leak)
- TelnetSession scope leaked on connect failure
- Host key + auth dialogs never wired up — silent TOFU, broken 2FA

Parser/renderer:
- DECSCNM (reverse screen mode) now rendered — swaps fg/bg screen-wide
- UTF-8 partial sequences buffered across chunk boundaries (11 new tests)

Hardcoded strings → string resources (EN/ES/SV):
- KeyManagerScreen (~22 strings), ConnectionListScreen (~24 strings)
- TerminalService/SessionNotifier notifications, SftpScreen, SftpViewModel
- SessionTabBar fallback label

Dead code removed:
- showSaveSnippet, qrDerivedKey, LANGUAGE_OPTIONS, LightColorScheme
- notifyOnReconnect preference, setPreviewTextColor, markActionTime
- anchorX/anchorY, production Log.d calls, 7 dead string resources

Code quality:
- SftpSession streams wrapped in .use{}, FileLogger.getLogFile respects
  useDownloads, FastSSHConfig unused keepalive removed, var→val fixes
- TabTypeAndLabelTest updated for TELNET/LOCAL enum values

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 19:55:28 +02:00
jima
65df2338a7 Launch intent profile auto-connect, PTY size fix, vttest parity test, screen dump
- Add --es profile "Name" launch intent to auto-connect by connection name/nickname
- Add --ez clearLog true intent extra to clear debug log on launch
- Add --ez dump true ADB broadcast to dump full screen buffer to log file
- Fix PTY allocation to use actual terminal dimensions instead of hardcoded 80x24:
  SSHSession.resize() now stores dimensions even while Connecting, and PTY
  allocation picks up the latest values instead of the initial config
- Add connectByProfile() to MainViewModel for profile-based connections
- Add VttestParityTest: replays recorded vttest session through both our engine
  and Termux at 80x24 and 102x38, verifying cell-by-cell parity (both pass)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 23:58:28 +02:00
jima
7c8cdd2b64 Fix double PTY resize by deduplicating before coroutine launch
SSHSession.resize() checked currentPtyCols/Rows before scope.launch
but updated them inside the launched coroutine, so rapid back-to-back
calls from onSurfaceReady and onDimensionsChanged both passed the check
and sent duplicate changeWindowDimensions to the SSH server. Move the
update before the launch so the second call sees the already-set values.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 00:27:17 +01:00
jima
a8ab99ffae SFTP file browser: full implementation replacing stub
lib-ssh — SftpSession:
- Wraps SSHJ SFTPClient with coroutines API on single-threaded dispatcher
- listDirectory (folders first, sort by name/size/date), downloadFile,
  uploadFile (64KB streaming with progress), createDirectory, rename,
  delete, canonicalize
- SftpEntry data class with Unix permissions string formatting
- Reuses existing SSHClient via newSFTPClient() — no second TCP connection

app — SftpViewModel:
- Directory browsing with currentPath/entries/isLoading/error StateFlows
- Transfer tracking: TransferState with ACTIVE/COMPLETE/FAILED/CANCELLED
- Large file confirmation (≥5MB) via confirmLargeDownload SharedFlow
- Upload, download, mkdir, rename, delete, sort order, download URI prefs

app — SftpActivity (replaces stub):
- Dark terminal aesthetic, programmatic layout
- Path breadcrumb bar with tappable segments
- File list: folder/file icons, name, permissions, size, date
- Tap folder to navigate, tap file to download, long-press context menu
- Bottom action bar: Upload (SAF multi-file) + Download folder (SAF tree)
- Transfer progress cards with direction badges, cancel, retry, auto-dismiss
- SAF integration: persistable URI permissions, stale URI recovery

app — TerminalService SFTP session management:
- openSftpSession/getSftpSession/closeSftpSession methods
- Auto-cleanup on parent SSH session disconnect

22 new unit tests (SftpSession, SftpViewModel, SftpEntrySort)
25 new SFTP string resources in EN/ES/SV

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 18:21:03 +01:00
jima
d5212addb9 Fast SSH config: replace DefaultConfig with minimal algorithm set
DefaultConfig is slow because it registers 31 ciphers and trial-inits
each via JCE. FastSSHConfig registers only modern algorithms (curve25519,
chacha20-poly1305, aes-ctr, sha2 MACs) — covers >99% of servers.
BouncyCastle moved to last provider so Android Conscrypt handles crypto.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 18:38:12 +01:00
jima
e1679ec5eb Selection magnifier, clipboard preview, context menu, port field fix
- SelectionMagnifier: zoomed chars around handle, green cursor highlight
  always visible (even on spaces), smooth velocity, no flickering
- Paste-only toolbar: clipboard text preview below Paste button
- Google search replaces snippet button in selection toolbar
- Toolbar follows visible teardrop handle dynamically
- Scrollbar only grabbable when visible from active scrolling
- Long-press host always shows full context menu (Connect, Disconnect
  All, Edit, Duplicate, Delete) regardless of active sessions
- Delete disconnects all sessions first
- Fix port field: remove select-all-on-recompose bug
- Add DefaultConfig timing log in SSHSession

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 17:35:31 +01:00
jima
3a07b5532a Keyboard settings dialog, disconnect info, render loop fix, WiFi-off crash fix
Keyboard settings (long-press space → gear):
- Tabbed dialog: Keyboard tab (size portrait/landscape, page indicators,
  color presets + hex picker, haptic, hints, repeat/longpress delays)
  and Quick Bar tab (position, size, color)
- Vertical QB overlays terminal from top to above KB
- Separate colors for KB and QB, custom hex input
- Pro gate: free users see settings read-only with upgrade banner
- Preview widget showing phone layout

Quick bar auto-repeat on long-press (arrows, Tab, PgUp/PgDn).

Disconnect info banner: shows classified reason (Socket lost,
Disconnected by host, etc.) + WiFi/mobile signal % in terminal.

Render loop: idle wait 500ms (was 16ms) — stops 60fps spin when idle.

WiFi-off crash: added SSHJ DisconnectListener on transport to catch
reader thread crashes. Wrapped WiFi lock + network status in try-catch.

Reconnect auth fix: password fallback to entry.password when
CredentialStore returns null. Added diagnostic logging.

Technical docs: docs/TECHNICAL.md (20KB, all modules).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 10:27:25 +01:00
jima
97de1821d8 Fix CSI > line wrap regression, session recovery, connection hardening
Revert treating > as a general CSI flag (broke line wrap at right edge).
Re-apply as targeted fix: CSI > sequences (kitty keyboard, modifyOtherKeys)
are consumed fully and silently ignored without affecting CSI dispatch.
Add session recovery via DataStore, SSH/Telnet reconnect logic,
disconnected-mode UI, DSR diagnostic logging, thread-safety annotations.
Add 8 regression tests (line wrap, CSI > handling) — 1883/1891 parity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 15:07:21 +01:00
jima
20bd135aa9 Mega audit: security, bugs, dead code, tests, file splits, emulator validation
Security:
- Remove PromiscuousVerifier fallback — now throws if hostKeyVerifyCallback not set
- Add SECURITY NOTE comments on credential handling, debug log capture, dev defaults
- Wire scrollbackLines preference to ScreenBuffer (was dead toggle)
- Remove unused EditText import, deprecated EXTRA_CONNECTION_ID

File splits (TerminalActivity 1439→995 lines):
- Extract TerminalConnectionHelper.kt (connect, reconnect, auto-save)
- Extract TerminalDisconnectedMode.kt (disconnected bar, save buffer)
- Extract TerminalSnippetHelper.kt (snippet picker, save)
- Add "large file by design" comments on remaining >700-line files

Tests (35 new tests):
- AuthOrderingTest: auth type hierarchy, toString safety, config builder
- ProFeaturesTest: free/pro feature gates, snippet/jump limits
- ProxyJumpCycleTest: cycle detection, chain building, edge cases
- TelnetIacTest: IAC stripping, NAWS encoding, escaped 0xFF

Emulator validation (portrait, API 34):
- Password SSH connection to office LAN server
- TOFU host key dialog — appears on first connect, silent on reconnect
- Terminal dimensions: stty size confirms real surface dimensions (55x54)
- vim: full screen rendering, clean exit without artifacts
- htop: full color process list rendering
- Custom keyboard: visible with quick bar (^C, TAB, arrows, ESC)
- Disconnected mode: error bar with Reconnect button works

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 06:58:08 +01:00
jima
38d29158ac Terminal engine parity, mouse reporting, security fixes, 1957 tests
Terminal engine (Phase 2 completion):
- Erase operations now inherit currentAttr (bg color, bold, etc.)
- DECALN (ESC#8) fills screen with 'E' per spec
- DEL (0x7F) ignored, CAN/SUB cancel escape sequences
- Invalid 256-color/truecolor values rejected instead of wrapping
- Mouse reporting: X10/Normal/ButtonEvent/AnyEvent modes with
  SGR/URXVT/X10 encoding (DECSET 9/1000/1002/1003/1006/1015)
- Jump buttons no longer trigger copy/paste menu on tap

Security (Phase 3+4):
- TOFU host key verification replaces PromiscuousVerifier — stores
  fingerprints in EncryptedSharedPreferences, prompts on first connect,
  warns on key change
- Password cached as CharArray, zeroed on disconnect (was String)
- Private key export blocked without biometric auth
- Password reveal blocked without biometric auth
- Backup rules exclude credentials and database

Bug fixes:
- PTY resize race: onSurfaceReady now reflows buffer immediately if
  dimensions mismatch (prevents blank screen on connect)

Tests: 1957 cases across 67 methods (1891 Termux parity, 22 mouse,
35 stress, 9 reflow) — 99.6% parity with Termux engine.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 08:59:26 +01:00
jima
d76f5aa05e Fix blank screen, add scrollbar, jump buttons, faster fling, reconnect UX
- Fix render race condition: pass onScreenUpdated callback into connectSSH/
  startLocalShell so it's set before the output collection job starts
- Remove LAYER_TYPE_SOFTWARE on SurfaceView that created an opaque layer
  hiding the Surface canvas
- Add proportional scrollbar on right edge (visible when scrollback > 0)
- Add jump-to-top/bottom overlay buttons that fade after 3s of inactivity
- Boost fling velocity 2.5x, reduce friction, increase max fling distance
- Add "Tap to reconnect" on clean session exit (was null click listener)
- Add SSH key management, connection manager UI, credential store,
  keyboard improvements, and database schema v2

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 13:42:52 +01:00
jima
b221a37bbf Add custom keyboard library, fix security issues and race conditions
Keyboard library (lib-terminal-keyboard):
- Custom Canvas-rendered keyboard with ViewPager2 infinite circular swiping
- JSON-driven layouts with language packs (EN/ES)
- Shift shows uppercase labels, long-press Shift for caps lock
- Long-press accent variants with improved popup (bigger cells, fixed coords)
- Sticky modifiers (Ctrl/Alt/Shift), combo keys, macro keys
- Quick bar, key repeat, haptic feedback, themes

Terminal fixes:
- Fix horizontal cutoff on first render when saved font size differs from default
- Add onDimensionsChanged callback for dynamic buffer resize

SSH security & stability:
- Eliminate temp file for KeyString auth (in-memory via OpenSSHKeyFile)
- Fix cleanup() race: cancel scope before clearing state
- Fix disconnect/timeout: set state before cleanup
- Use AtomicLong for lastWriteTime/lastReadTime thread safety
- Shutdown writeExecutor in cleanup to prevent thread leak

App fixes:
- Wakelock with 4-hour safety timeout
- CredentialStore uses .commit() instead of .apply()
- FileLogger reuses SimpleDateFormat instance
- Theme cycling cancels previous save coroutine
- Listener null safety in ScreenBuffer/BaseTermParser/XtermParser
- Clear externalLogger in onDetachedFromWindow to prevent leak

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 12:31:17 +01:00
jima
9b99b40191 Refactor terminal view, add themes, selection handles, fix keepalive
Split TerminalSurfaceView (659 lines) into 5 focused classes:
- TerminalRenderer: Canvas drawing, color palettes, selection highlight
- TerminalGestureHandler: scroll, pinch-to-zoom, long-press, double-tap
- TerminalInputHandler: IME InputConnection, key-to-bytes translation
- TerminalTextSelection: selection state, draggable handles, word select
- TerminalSurfaceView: thin orchestrator (~313 lines)

Add 7 built-in terminal themes (Default Dark, Dracula, Monokai, Nord,
Solarized Dark, Solarized Light, Gruvbox) with long-press FAB to cycle.
Theme selection persisted in DataStore.

Add text selection with draggable handles: long-press to start, drag
handle circles to adjust start/end, ActionMode with Copy/Select All/
Paste, bracketed paste support.

Fix keepalive monitor: now actually disconnects dead connections instead
of just logging warnings. Active TCP probe via sendUrgentData() when
read silence exceeds 90s. Clear logs after export to Downloads.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 23:57:15 +01:00
jima
32891865a0 Fix zombie SSH connections: keepalive, write timeout, auto-reconnect
- Switch SSHJ from HEARTBEAT to KEEP_ALIVE provider (tracks responses,
  disconnects after 3 missed keepalives / 45s)
- Add 10s write timeout via executor — detects dead TCP sockets
- Add connection health monitor: logs keepalive status every 15s,
  warns on 60s read silence after write
- Auto-reconnect on unexpected disconnect with backoff (5s, 10s, 30s)
- Show countdown in status overlay, tap to reconnect immediately
- Cache password in memory for auto-reconnect, clear on user disconnect

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:21:07 +01:00
jima
489d6570a3 Fix race conditions, resource leaks, and add notification permission
Bug fixes across all modules:
- SSHSession: cancel coroutine scope on disconnect, atomic connect() with mutex,
  synchronize shellOutputStream nulling with writeLock
- TerminalService: synchronize parser access during replaceEngine/output collection,
  remove wakelock timeout for long sessions
- LocalShellSession: fix FD leak on init failure, cancel scope on stop
- TerminalSurfaceView: guard charWidth/charHeight against zero (div-by-zero)
- DatabaseModule: remove destructive migration fallback
- SSHConnectionConfig: add port range validation
- TerminalActivity: add POST_NOTIFICATIONS permission + runtime request,
  deduplicate reconnect logic, remove unused savedId parameter
- Refactor parser into BaseTermParser/Vt220Parser/XtermParser hierarchy
- Add ProGuard rules, BuildConfig.DEV_DEFAULTS, release minification

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:31:48 +01:00
jima
ffc2f94e9b Add foreground service, SSH keepalive, extra keys bar, natural scroll
- TerminalService: foreground service owns SSH/shell sessions, survives
  activity backgrounding, persistent notification, partial wakelock
- SSH keepalive: 15-second interval prevents idle disconnects
- ExtraKeysBar: two-row scrollable bar (CTRL/ESC/TAB/arrows/F1-F12)
  with sticky CTRL toggle, shown above IME keyboard
- Natural scroll: swipe down scrolls into history (mobile convention)
- Disconnect overlay: shows "tap to reconnect" with saved credentials
- Activity refactored to bind/unbind service lifecycle
- Termux parity tests and stress tests added

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:00:06 +01:00
jima
ba224fe79b first commit 2026-03-22 18:01:28 +01:00