Commit graph

44 commits

Author SHA1 Message Date
jima
7b68e6404b Audit 2026-04-12: C++ hardening, crash fix, dead code, quality
Fifth full codebase audit across all five modules (lib-ssh, lib-terminal-view,
lib-terminal-keyboard, lib-vault-crypto, app).

Security:
- Added cppFlags to lib-vault-crypto build — vault_crypto.cpp JNI bridge was
  missing all compiler hardening flags (-fstack-protector-strong, -D_FORTIFY_SOURCE=2)

Bugs fixed:
- SessionNotifier crash: first{} → firstOrNull to prevent NoSuchElementException
- Keyboard modifiers not consumed on SwitchPage/ToggleNumBlock — armed CTRL/ALT
  would persist and incorrectly modify the next key press
- KeyManagerViewModel silent exception swallow — now logs errors via FileLogger
- TelnetSession.sendTerminalType() variable shadowing fix

Dead code removed:
- Vt100Parser empty class (Vt220Parser now extends BaseTermParser directly)
- XtermParser.sendPrimaryDA() redundant override (identical to parent)
- TerminalKeyboard dead fields: menuPopupActive, menuPopupItems, miniContainer
- SpecialAction.SETTINGS_OPENED never emitted
- Deprecated 3-arg saveHostKeyFingerprint overload (no callers)

Code quality:
- Color(0xFF6E7979) → AppColors.Muted in ConnectionListScreen
- Hardcoded "v1.0.0" → BuildConfig.VERSION_NAME in SettingsScreen
- SubscriptionScreen back button contentDescription for accessibility
- TAG → companion const val in StartupCommandRunner, PortForwardManager, SftpSessionManager
- TerminalRenderer swapped KDoc comments fixed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 15:09:05 +02:00
jima
e0cbcc6d43 Settings from terminal view, scrollback truncation dialog, drawer about footer
- Add "Settings" item to tab bar and drawer bar kebab menus — opens full
  SettingsScreen from the terminal view via NavHost navigation
- Add Settings row + compact about footer (app icon, version, developer)
  to drawer content panel
- Scrollback truncation: when user lowers scrollback value with active
  SSH/Telnet/Local sessions, confirmation dialog warns about history loss.
  ScreenBuffer.truncateHistory() trims oldest lines and clamps scrollOffset.
- TerminalService.truncateAllScrollback() iterates all session buffers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 11:45:20 +02:00
jima
bda8967ab1 Key import validation, 20 themes, 10 fonts, settings redesign, cursor speed/blink, terminal type
Key Import:
- SSHKeyLoader validates imported keys, extracts real pubkey/fingerprint
- ZIP file support with password handling (zip4j)
- Import chooser dialog: ZIP or single file
- Results dialog: valid/encrypted/invalid sections

Theme & Font:
- 20 color themes (10 new: Ayu Dark, Catppuccin Mocha, Everforest Dark, etc.)
- 10 bundled monospace fonts (JetBrains Mono, Fira Mono, Hack, etc.)
- Combined ThemePickerSheet: color scheme combo, font combo, font size slider
- Live preview with scrollable terminal output, adapts to landscape
- Typeface support in TerminalRenderer/TerminalSurfaceView

Settings:
- Material 3 grouped card layout (7 sections)
- Cursor speed: Slow/Normal/Fast/Rapid with key repeat acceleration
- Cursor blink: 530ms toggle, 15s idle auto-stop
- Keep screen on wired to FLAG_KEEP_SCREEN_ON
- Removed Show session tab bar toggle (always visible)
- Font size stepper replaced slider in settings

Terminal:
- Per-connection terminal type (TERM env var) in Edit Connection
- DB migration v1→v2 for termType column
- configChanges prevents activity recreation on rotation

Fixes:
- Vault import hosted in-place (no popBackStack)
- Clickable radio/checkbox/switch rows (text tappable)
- Password rules shown above field in vault export
- Deploy script cleans all stale APKs
- Telnet uses per-connection termType

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 02:01:06 +02:00
jima
ca16651d69 Security audit: TOFU hardening, FLAG_SECURE, paste sanitization, compiler hardening
Production security audit (OWASP-aligned, prod-only scope):
- Default TOFU to REJECT when no UI handler (prevents silent accept during service-start window)
- Add FLAG_SECURE with preventScreenCapture preference (default ON, Settings → Security)
- Sanitize bracketed paste content (strip \e[200~/\e[201~ to prevent paste-escape injection)
- Add VaultCrypto ProGuard keep rule (prevents R8 stripping JNI methods in release)
- Create network_security_config.xml (system CAs only, cleartext disabled)
- Add compiler hardening flags to both native modules (-fstack-protector-strong, -D_FORTIFY_SOURCE=2)
- Set EXTRA_IS_SENSITIVE on all clipboard writes (terminal copy, key copy, SFTP path copy)
- Remove file:// from URL detection (prevents local file access via crafted terminal output)
- Verify signing certificate in pro APK migration (prevents fake APK granting free features)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:04:05 +02:00
jima
17250598b1 Subscription model, dev/prod flavors, vault import fix, font default
Major architecture change: single APK with Google Play Billing
subscriptions (monthly/yearly/lifetime) replacing free/pro build
flavors. Runtime feature gating via SubscriptionProFeatures with
grandfathering support. Dev/prod flavor split isolates all debug
infrastructure (ADB receiver, test profiles, log icons) from
production builds via DevConfig with const val dead code elimination.

- billing/: BillingManager, SubscriptionRepository, SubscriptionStore, SubscriptionState
- SubscriptionProFeatures replaces FreeProFeatures/FullProFeatures
- SubscriptionScreen for in-app purchase UI
- Activity-alias icon switching (teal free / gold pro)
- Pro APK migration detection
- Vault import: fix credential/key/jumpHost re-linking (id=0 bug)
- DB reset to v1 (new package, no migrations needed)
- Default font 10sp (was 14sp in library), global pref applied to new sessions
- Log download no longer clears the log (only Clear does)
- Active session timer always visible (not gated behind sessionTracking)
- Build timestamp shows version number (dev only)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 15:01:26 +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
256d059d51 QB app shortcuts, Canvas key icons, bounded scroll, alt buffer reflow fix
Quick Bar:
- App shortcuts button (W icon, far-left) opens two-level popup:
  vim/nano/tmux/scr/F1-12 → 3-column key map grid
- AppShortcutsPopupView: Canvas-based, direction-aware, tap-to-select
- PopupWindow replaced with DecorView overlay + onBackPressedDispatcher
  + layout listener for AKB close detection (one BACK closes both)
- W button toggles popup; toggle_mod actions keep popup open
- Canvas-drawn key icons (tab, backtab, arrows, pgup, pgdn) via labelIcon
- Bounded scroll mode (infiniteScroll=false default)
- CTRL, HOME, END, PGUP, PGDN added to QB scrollable keys
- Menu keys (vim/nano/tmux/screen) removed from scrollable area

Tab bar:
- + button replaced with kebab menu (New Session + KB/QB Settings)
- KB Settings label adapts to keyboard mode (CKB vs AKB)

Bug fixes:
- Alt buffer reflow: main screen content preserved during resize when
  vim/htop is active (was lost on navigate away + return)
- Quick-connect: no saved connection lookup for connectionId=0, tab
  label shows user@host instead of saved connection nickname
- ADB broadcast: enter fires independently from text

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:53:54 +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
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
1a35a9b57f Post-audit cleanup: i18n, Hilt DI, oversized file extractions
- Extract 40+ hardcoded strings from TerminalDialogs, FreeProFeatures,
  KeyboardSettingsDialog, VaultExportSheet, VaultImportSheet to strings.xml
  with EN/ES/SV translations. SSH technical labels kept in English.
- Add StringResolver interface for ViewModel localization (injected via Hilt)
  — fixes hardcoded strings in VaultExport/Import, Main, KeyManager ViewModels.
- TerminalService: replace duplicate Room instance with @AndroidEntryPoint
  Hilt-injected DAOs. Extract SftpSessionManager, PortForwardManager,
  StartupCommandRunner into standalone classes.
- EditConnectionScreen: extract PortForwardSection (bottom sheet, rows, dialogs).
- ConnectionListScreen: extract ConnectionItemCard and TimeFormatUtils.
- TerminalSurfaceView: extract MouseReporter.
- Delete TODO2.md (all items completed). Update TODO.md, TECHNICAL.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 22:17:44 +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
9e5c3100e2 Session stability, logging, disconnect notifications, save output, pinch-zoom in mouse mode
- Fix foreground service lifecycle: keep service alive while disconnected sessions exist,
  preventing Android from killing sessions when app is backgrounded during WiFi drops
- Fix cleanupDisconnectedSession: keep sessions in _activeSessions map so UI shows
  disconnect bar and tab indicator instead of silently removing them
- FileLogger: append mode (persists across app opens), fallback to app-private dir,
  ZIP export with .bin recordings, comprehensive trace logging across all components
- SessionNotifier: batched disconnect notifications (5s window), auto-cancel on reconnect,
  smart handling of partial reconnects (5 disconnect, 3 reconnect → shows 2 remaining)
- Save Output: export scrollback + screen to Download/<alias>_<timestamp>.txt
- Disconnect bar: styled pill buttons, timestamps on all disconnect/reconnect messages
- Session tab bar: red accent + dark red background for disconnected tabs
- In-app snackbar: brief "Duero disconnected" alert when session drops while in app
- Pinch-zoom in mouse mode: scale detector fed before mouse intercept, multi-finger
  gestures bypass mouse reporting so htop/vim resize works
- Snippet manager: duplicate action, list refreshes after edit/delete/duplicate
- Notify on disconnect setting (default on), build timestamp on connection list
- Deploy script fix, SessionRecorder rolling reset at 1MB

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 15:13:15 +02:00
jima
40650286dc VT52 compatibility mode with DEC Special Graphics charset
Implement VT52 mode as a standalone delegate class (Vt52Parser), activated
via CSI ?2l (DECANM reset) and exited via ESC <. Supports all VT52 escape
sequences: cursor movement (A-D), direct addressing (ESC Y row col), erase
(J/K), reverse index (I), home (H), and DECID response (ESC /Z).

Graphics character mode (ESC F/G) maps ASCII 0x5F-0x7E to DEC Special
Graphics glyphs (box-drawing, math symbols) — same charset as VT100's G0 '0'.

37 unit tests, all vttest tests 1-11 now pass including test 7 (VT52 mode).
New docs/TERMINAL_PARSER.md with full parser architecture, state machine,
DECCOLM quirks, VT52 implementation details, and graphics charset table.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 09:47:40 +02:00
jima
e72c4de55d Fix DECCOLM mid-chunk data loss and CSI leading zeros truncation
replaceEngine now updates the old parser's screen reference so remaining
bytes in a chunk after DECCOLM are written to the new buffer instead of
the discarded one. Also bumps MAX_PARAM_LEN 10→16 so vttest's 11-digit
leading-zero parameters parse correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 01:06:41 +02:00
jima
b2d8354ebf DECCOLM support: 80/132 column mode switching for vttest compatibility
- Handle CSI?3h (132-col) and CSI?3l (80-col) in setDecPrivateMode:
  clear screen, reset scroll margins, home cursor, fire listener callback
- Add onColumnModeChange callback to TerminalListener interface
- TerminalService handles DECCOLM by reflowing ScreenBuffer to requested
  column count and sending PTY resize to the SSH server
- Add onBufferReplaced callback so TerminalSurfaceView picks up the new
  ScreenBuffer immediately (not just on next Compose recomposition)
- Add DECCOLM parity tests verifying match with Termux behavior
- vttest test 1 now renders correctly on terminals wider than 80 columns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 00:33:10 +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
70ab307294 Pinch-to-zoom fixes, telnet tab color, SFTP simplify, render reliability
Zoom: defer commit to ACTION_UP so ScaleGestureDetector min-span restarts
don't interrupt continuous zoom-out. Suppress tap after pinch to prevent
AKB appearing on finger lift. Thread-safe font metrics (@Volatile).

Tabs: telnet sessions show violet accent, detect session type (SSH/Telnet/
Local) via TabType enum. Connection list now sorts by last-used (call
updateLastConnected on connect).

SFTP: simplify large download confirmation (inline threshold check),
AndroidViewModel for string resource access, proper error on null stream.

Render: consume dirty after lockCanvas, re-request on lockCanvas failure,
extra render after zoom settle. Default font size 10sp. Dismiss soft
keyboard when navigating to NavHost. Paste preview color matches keyboard
hint (#888888).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 09:42:15 +02:00
jima
cdeb3a5045 SFTP tabs in Layer 1, fix dual-keyboard bug, add keyboard diagnostics
SFTP is now rendered as an independent tab (amber accent) in the terminal
layer instead of a NavHost route. Tabs support duplicate, rename, close.

Fix dual-keyboard bug where system keyboard appeared on top of custom
keyboard: set softInputEnabled=false on TerminalSurfaceView in CKB mode
so tapping the terminal no longer triggers the IME.

Add diagnostic logging for keyboard show/hide transitions, softInputEnabled
changes, and DataStore keyboard type persistence to debug field reports.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 19:51:39 +02:00
jima
ece519d768 Security audit, dead code sweep, SOCKS5 dynamic forwarding, 120+ tests
Security fixes:
- Password zeroing in connectSSHInternal catch block (jump chain failures)
- API level guard for TelephonyManager.signalStrength (min SDK 27 vs API 28)
- Port forward delete confirmation dialog added to EditConnectionScreen

Dead code removal (95 entries):
- 88 unused string resources from EN/ES/SV
- 5 unused color resources (Android template defaults)
- 2 unused XML resources (backup_rules, data_extraction_rules)

DRY refactoring:
- EditConnectionViewModel: buildSavedConnection(), saveOrDeletePassword()
- TerminalDisconnectedMode: createBarActionButton()

SOCKS5 dynamic port forwarding (new feature):
- Socks5Proxy.kt: RFC 1928 + RFC 1929 SOCKS5 proxy server
- CONNECT command with IPv4/IPv6/domain, NO_AUTH + username/password auth
- 64KB relay buffers, thread-per-connection, proper half-close
- Specific error codes: ConnectException→0x05, UnknownHostException→0x04
- TerminalService DYNAMIC branch now opens real SOCKS5 tunnel via
  SSHClient.newDirectConnection() per SOCKS5 CONNECT request
- UI: default port 1080 pre-fill, SOCKS5 hint text

Port forwarding verification:
- Schema, DAO, UI, ViewModel, tunnel activation all verified
- Confirmed NOT gated by ProFeatures (free + pro both have access)

Test coverage (120+ new tests):
- Unit: CredentialStore, KeyGenerator, ScreenBuffer, VtParser,
  ModifierStateManager, TelnetSession, JumpChain, AutoReconnect,
  ProFeatures, PortForwardDao, TunnelLifecycle, Socks5Proxy,
  FieldVisibility, FreeGate
- Instrumented: CredentialStore on-device, Room migrations 1-10,
  BiometricAuthManager timeout, ConnectionManager launch,
  CASCADE delete, port forward CRUD, SOCKS5 flow
- docs/SOCKS5_TEST.md: manual smoke test commands

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 16:32:17 +01:00
jima
34fd76e48d System keyboard quick bar, selection snap, ExtraKeysBar removal, UX fixes
- Remove ExtraKeysBar entirely — quick bar from custom keyboard now serves
  both keyboard modes, always visible
- System keyboard mode: quick bar always shown, custom keyboard container
  hidden, no longer forced visible on Connected state re-observation
- Selection handle snap-to-content on drop: end handle snaps backward to
  last non-whitespace, start handle snaps forward to next content line
- Suppress IME suggestions with VISIBLE_PASSWORD input type
- Context menu: show "New Session" (green) instead of "Connect" when host
  has active sessions
- Fix APK date: resolve date at task execution time, not configuration
  time, so it's never stale from Gradle config cache

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 08:12:01 +01:00
jima
7b1a795008 Reconnect buffer preservation, network-aware auto-reconnect, launcher fix
- Preserve terminal screen buffer across reconnect for both SSH and Telnet
  sessions (was only working for SSH triggerReconnect path)
- Add network awareness to auto-reconnect: wait for connectivity before
  counting attempts, reset counter when network returns
- Add ACCESS_NETWORK_STATE permission for ConnectivityManager callbacks
- Fix launcher icon reopening: detect MAIN/LAUNCHER intent in
  ConnectionManagerActivity and finish() to reveal existing TerminalActivity
- Fix spurious blank line on resume: only write separator on
  Connecting→Connected transition, not on re-observation
- Guard performEditorAction to only send Enter for explicit IME actions
- Add reconnecting_in and waiting_for_network strings (EN/ES/SV)
- Strengthen CLAUDE.md strings.xml rule

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 21:55:06 +01:00
jima
f58e587a9a Inline snippet create, softer haptic, wrapped copy fix, font size removal
Snippets:
- Inline create form in snippet picker (no separate dialog)
- Name optional, command required
- Global/per-host checkbox (remembered, defaults to current host)
- List auto-refreshes after save
- Full-screen snippet picker
- Snippet tap sends command first, enter after 150ms delay

Selection:
- Wrapped word copy no longer inserts newline between wrapped rows
- Paste-only toolbar: tap anywhere triggers paste (not just button)

Settings:
- Remove font size slider (pinch-to-zoom is sufficient)
- Softer haptic feedback (CLOCK_TICK instead of KEYBOARD_TAP)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:12:34 +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
68ce7c5552 Major UX overhaul: selection, themes, settings, dark mode, per-host auto-reconnect
Selection & toolbar:
- Extract SelectionToolbar class from TerminalSurfaceView
- Multi-line word selection (follows wrapped lines)
- Long-press empty space = paste-only at cursor, space between text = select line
- Google search button replaces snippet in toolbar
- Toolbar follows visible teardrop handle dynamically
- Block scroll/scrollbar during active selection
- Scrollbar only grabbable when already visible from scrolling
- Compact toolbar: smaller text/padding

Settings & per-host:
- Auto-reconnect per-connection (DB v9), global as default
- Theme picker dialog with live terminal preview (ls -l --color)
- Force dark theme everywhere (XML + Compose), dark splash screen
- Keyboard settings: fix scroll in BottomSheet, non-draggable
- Unsaved changes prompt (Save/Discard/Cancel) in KB settings + edit connection
- All hardcoded strings → strings.xml (EN/ES/SV)
- Biometric lock warns when no device lock configured
- QB position "Hidden" option, remove drag handle
- Port field selects-all on focus
- Keyboard height loaded upfront (no resize flash)
- Global defaults properly applied on connect
- Vertical QB insets terminal view, not keyboard

Fixes:
- Remove "No ScreenBuffer" debug text
- Bottom sheets: skipPartiallyExpanded, dark containerColor
- Dead code: hapticFeedback from KB settings, outputTextView clears
- Snippet tap = execute (with enter), long-tap for insert/edit/delete
- Default Dark blue lightened (#6E7EFF / #9EA8FF)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:46:14 +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
73ccea0a1e Add WiFi lock per-connection + global default, fix zoom overlay to rows×cols
WiFi lock: per-connection wifiLock field (Room migration v7), global
wifiLockDefault in DataStore, WIFI_MODE_FULL_HIGH_PERF acquired on
connect / released on disconnect+error+onDestroy. Hidden for local
shell, visible for SSH and Telnet. Info dialog on label tap.

Zoom overlay: dimension display now shows rows×cols (stty order)
instead of cols×rows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 21:12:12 +01:00
jima
ecac9dd463 Session recorder, CSI </> states, fix OSC 0x9C title leak
Add debug-only byte stream recorder (SessionRecorder) with auto-recording
per session, MediaScanner integration, and replay test infrastructure.

Add CSI_GT/CSI_LT parser states for \e[> and \e[< private sequences —
DA2 response, kitty keyboard, modifyOtherKeys handled without affecting
general CSI parsing. Fixes \e[<u being dispatched as restoreCursor.

Fix OSC title leak: 0x9C (8-bit ST) was terminating OSC mid-UTF-8
sequence (✳ = E2 9C B3), causing " Claude Code" to print as visible
text. Removed 0x9C check — only BEL and ESC\ terminate OSC.

105 tests pass, 1883/1891 parity (99.6%), 2 session replay goldens.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 19:18:37 +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
05f85e14fe Add OverScroller.forceFinished(true) on ACTION_DOWN for fling stop
Adds an OverScroller instance to TerminalGestureHandler and calls
forceFinished(true) on touch down alongside the existing custom fling
stop logic. This is the standard Android pattern for immediately
halting scroll momentum when the user touches the screen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 07:38:06 +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
23fcfd2b75 Free/Pro product flavors, keyboard UX improvements, dimension overlay
Product flavors:
- Free (com.roundingmobile.sshworkbench) and Pro (com.roundingmobile.sshworkbench.pro)
- ProFeatures interface with Hilt DI — all branching via interface, no BuildConfig checks
- Free stubs: biometric lock, language packs, startup commands, session tracking,
  save buffer, mouse reporting, keyboard customization gated with upgrade prompt
- Pro icon uses gold background to distinguish from free (green)
- ProGuard fix for EdDSA sun.security.x509 reference

Keyboard improvements:
- Key press hint stays visible while finger is down (no 500ms auto-hide)
- First row keys show hint below key (was clipped above container)
- Hint respects Shift state (shows uppercase when shifted)
- Page swiping disabled during key press to prevent accidental page switches
- Quick bar arrows match main keyboard glyphs (filled triangles)
- Page indicator dots: colored per page, tappable for direct navigation, larger spacing

Terminal improvements:
- Dimension overlay (cols x rows) shown during pinch-to-zoom and resize
- Selection auto-scroll acceleration based on drag distance past edge
- Copy toolbar consumes all touch events to prevent re-triggering selection
- Reconnect fallback uses saved connection when session entry is cleaned up
- Mouse reporting gated via TerminalSurfaceView.mouseReportingEnabled flag

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 20:44:35 +01:00
jima
b93e54ac36 URL detection, selection improvements, handle crossing fix
- Add TerminalUrlDetector: detect and underline URLs in terminal output
- Tap URL shows Open/Copy dialog instead of auto-opening
- Long press selects word (min 3-char span on whitespace)
- Double tap selects entire row
- Prevent selection handles from crossing each other
- Fix double-tap char read when scrolled back in history

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 16:17:23 +01:00
jima
c2f23331d6 Selection auto-scroll, save dialog with connection name
- Auto-scroll viewport when dragging selection handle to top/bottom edge,
  allowing selection across entire scrollback buffer without lifting finger
- Save session dialog with editable filename using connection nickname/name
  instead of raw IP address (e.g. Hello_20260325_143022.txt)
- Larger copy/paste toolbar buttons and density-aware selection handles

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:58:24 +01:00
jima
327a150ca1 Session tracking, Material 3 dialogs, disconnected mode, auto-reconnect setting
- Store session start/end/duration in DB (migration v2→v3), display formatted
  times in connection list with live timer for active sessions
- Detect 12/24h device setting for all time formatting
- Replace all AlertDialog.Builder dialogs with Material 3 styled TerminalDialogs
  (host key, auth prompt, connect, clean exit, connection actions)
- Upgrade app theme to Theme.Material3.DayNight.NoActionBar with terminal colors
- Add auto-reconnect setting (default off) in Settings → Terminal
- Disconnected "Stay" mode: hide keyboard, show compact bottom bar with
  reconnect button and save-to-file button, terminal stays interactive for
  scroll/select/copy. Buffer saved to Downloads/SshWorkbench/
- Fix session lifecycle: clean up dead sessions from activeSessions map,
  properly clean up on back press, reset state flags on reconnect
- Larger selection handles (density-aware 14dp) and toolbar buttons (min 14dp
  text, more padding, 12dp corners)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:06:49 +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
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