diff --git a/.gitmodules b/.gitmodules index 258a380..bce3c62 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "controller/termux-core/termux-source"] path = controller/termux-core/termux-source - url = https://github.com/iiab/termux-app.git + url = https://github.com/appdevforall/termux-app.git diff --git a/controller/app/src/main/java/org/iiab/controller/IIABExtraKeys.java b/controller/app/src/main/java/org/iiab/controller/IIABExtraKeys.java new file mode 100644 index 0000000..1cdd075 --- /dev/null +++ b/controller/app/src/main/java/org/iiab/controller/IIABExtraKeys.java @@ -0,0 +1,93 @@ +package org.iiab.controller; + +import android.util.Log; + +import com.termux.shared.termux.extrakeys.ExtraKeysConstants; +import com.termux.shared.termux.extrakeys.ExtraKeysInfo; +import com.termux.shared.termux.extrakeys.ExtraKeysView; + +import org.json.JSONException; + +/** + * IIAB default extra-keys layout for the embedded Termux terminal. + * + *

This logic previously lived as {@code loadIIABDefaultKeys()} added directly to the + * vendored upstream class {@code ExtraKeysView} (in the {@code termux-source} submodule). + * It only uses public Termux APIs ({@link ExtraKeysView#reload(ExtraKeysInfo, float)} and + * the public {@link ExtraKeysInfo} constructor), so it now lives in the app instead. That + * keeps the Termux fork a clean mirror of upstream and avoids merge conflicts on every + * upstream sync (see {@code controller/docs/FORK_DELTA_ANALYSIS.md}, finding K1). + */ +public final class IIABExtraKeys { + + private static final String TAG = "IIAB-ExtraKeys"; + + /** + * Two-row extra-keys layout. These keys must stay in sync with the keys handled by + * {@code MainActivity}'s {@code IExtraKeysView} click listener. Single source of truth. + */ + public static final String DEFAULT_LAYOUT = + "[\n" + + " ['ESC', '/', '-', 'HOME', 'UP', 'END', 'PGUP'],\n" + + " ['TAB', 'CTRL', 'ALT', 'LEFT', 'DOWN', 'RIGHT', 'PGDN']\n" + + "]"; + + /** + * Minimal one-row layout applied only if {@link #DEFAULT_LAYOUT} fails to load, so the + * user still gets usable keys instead of none (finding K4). + */ + static final String FALLBACK_LAYOUT = + "[['ESC', 'TAB', 'CTRL', 'LEFT', 'DOWN', 'UP', 'RIGHT']]"; + + private IIABExtraKeys() { + // Utility class — no instances. + } + + /** + * Loads the IIAB default extra-keys layout into the given view via the public + * {@link ExtraKeysView#reload(ExtraKeysInfo, float)} API. If the default layout fails + * for any reason it falls back to {@link #FALLBACK_LAYOUT} so the terminal still shows + * usable keys. No-op if the view is null. + * + * @param extraKeysView the terminal's extra-keys view + */ + public static void apply(ExtraKeysView extraKeysView) { + if (extraKeysView == null) { + return; + } + if (tryLoad(extraKeysView, DEFAULT_LAYOUT)) { + return; + } + Log.w(TAG, "IIAB default extra-keys layout failed to load; applying minimal fallback"); + if (tryLoad(extraKeysView, FALLBACK_LAYOUT)) { + return; + } + Log.e(TAG, "Both IIAB and fallback extra-keys layouts failed to load; no extra keys applied"); + } + + private static boolean tryLoad(ExtraKeysView extraKeysView, String layout) { + try { + extraKeysView.reload(buildInfo(layout), 0f); + return true; + } catch (Exception e) { + Log.e(TAG, "Failed to load extra-keys layout", e); + return false; + } + } + + /** + * Builds an {@link ExtraKeysInfo} from a layout string using the same parameters as + * {@link #apply}. Package-private so unit tests can validate {@link #DEFAULT_LAYOUT} + * and {@link #FALLBACK_LAYOUT} without needing an Android view (finding K5). + * + * @param layout the extra-keys layout string + * @return the parsed {@link ExtraKeysInfo} + * @throws JSONException if the layout string is malformed + */ + static ExtraKeysInfo buildInfo(String layout) throws JSONException { + return new ExtraKeysInfo( + layout, + "default", + new ExtraKeysConstants.ExtraKeyDisplayMap()); + } +} diff --git a/controller/app/src/main/java/org/iiab/controller/MainActivity.java b/controller/app/src/main/java/org/iiab/controller/MainActivity.java index 912f798..2b6c068 100644 --- a/controller/app/src/main/java/org/iiab/controller/MainActivity.java +++ b/controller/app/src/main/java/org/iiab/controller/MainActivity.java @@ -2033,7 +2033,7 @@ public void logStackTrace(String tag, Exception e) { findViewById(R.id.extra_keys_view); if (extraKeysView != null) { - extraKeysView.loadIIABDefaultKeys(); + IIABExtraKeys.apply(extraKeysView); // Listen for normal keys (ESC, TAB, UP, etc) extraKeysView.setExtraKeysViewClient(new com.termux.shared.termux.extrakeys.ExtraKeysView.IExtraKeysView() { diff --git a/controller/app/src/test/java/org/iiab/controller/IIABExtraKeysTest.java b/controller/app/src/test/java/org/iiab/controller/IIABExtraKeysTest.java new file mode 100644 index 0000000..2afbd4a --- /dev/null +++ b/controller/app/src/test/java/org/iiab/controller/IIABExtraKeysTest.java @@ -0,0 +1,37 @@ +package org.iiab.controller; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import com.termux.shared.termux.extrakeys.ExtraKeysInfo; + +import org.junit.Test; + +/** + * Unit tests for {@link IIABExtraKeys} (finding K5). These validate that the layout + * constants parse into the intended grid shape, catching a malformed layout edit before + * it ships. Runs on the plain JVM via {@code testOptions.unitTests.returnDefaultValues} + * + the {@code org.json} test dependency added in Phase 0. + * + *

Assertions are limited to grid structure (rows × keys), which is the part most likely + * to break on a bad edit and is independent of Android framework stubs. + */ +public class IIABExtraKeysTest { + + @Test + public void defaultLayout_parsesIntoTwoRowsOfSevenKeys() throws Exception { + ExtraKeysInfo info = IIABExtraKeys.buildInfo(IIABExtraKeys.DEFAULT_LAYOUT); + assertNotNull(info); + assertEquals("default layout should have 2 rows", 2, info.getMatrix().length); + assertEquals("row 1 should have 7 keys", 7, info.getMatrix()[0].length); + assertEquals("row 2 should have 7 keys", 7, info.getMatrix()[1].length); + } + + @Test + public void fallbackLayout_parsesIntoSingleRowOfSevenKeys() throws Exception { + ExtraKeysInfo info = IIABExtraKeys.buildInfo(IIABExtraKeys.FALLBACK_LAYOUT); + assertNotNull(info); + assertEquals("fallback layout should have 1 row", 1, info.getMatrix().length); + assertEquals("fallback row should have 7 keys", 7, info.getMatrix()[0].length); + } +} diff --git a/controller/docs/TECH_DEBT_PLAN.md b/controller/docs/TECH_DEBT_PLAN.md index bb05fdc..3c5b1a3 100644 --- a/controller/docs/TECH_DEBT_PLAN.md +++ b/controller/docs/TECH_DEBT_PLAN.md @@ -2,6 +2,25 @@ > Consolidated, English-language successor to `controller/TECH_DEBT_AUDIT.md` (previously Spanish). Produced from four parallel line-level audits — UI/lifecycle, deploy/install, sync/ADB, and monitoring + build/infra. Scope: 34 Java files, ~11,707 LOC under `app/src/main/java/org/iiab/controller/`. Date: 2026-06-16. Repo status: proof-of-concept. See `docs/ARCHITECTURE.md` for how the module works. +## Progress log + +_Last updated: 2026-06-16. Tracks remediation work against the findings below. IDs map to the register in this file (F/D/S/M) and to `FORK_DELTA_ANALYSIS.md` (K)._ + +**Phase 0 — Guardrails: DONE** (PR `chore/phase0-guardrails`) +- Extracted `SystemStatsUtil` and added the first JVM unit tests (`SystemStatsUtilTest`, `SyncHandshakeHelperTest`); added unit-test infra (`returnDefaultValues` + real `org.json`). Addresses **M10**. +- CI gate: blocking `testDebugUnitTest`; `:app:lintDebug` runs non-blocking (scoped to `:app`). Addresses **M11**. +- Added a root `.gitignore` and `FORK_DELTA_ANALYSIS.md`. +- Remaining: flip lint `abortOnError=true` once the `:app` lint backlog is triaged; broaden tests to more pure functions (`LogManager.getFormattedSize`, `InstallationPlanner` sizing, the YAML parser). + +**K1 — Fork delta (Termux ExtraKeys): IN PROGRESS** (PR `feat/k1-extrakeys-in-app`; details in `FORK_DELTA_ANALYSIS.md`) +- **K1**: `loadIIABDefaultKeys()` moved out of upstream `ExtraKeysView` into app `IIABExtraKeys` (public APIs only). DONE (app side). +- **K3**: layout is now a single-source-of-truth constant. DONE. +- **K4**: falls back to a minimal layout if the default fails to load. DONE. +- **K5**: unit test validating the layout grid (`IIABExtraKeysTest`). DONE. +- Remaining: point the submodule to `appdevforall/termux-app` at clean upstream and commit the pointer. + +**Phases 1–4: NOT STARTED.** Next: Phase 1 security — **D2**, **D6**, **S1**, **S3**, **M4**, **D12**. + ## 1. Executive summary The Controller is functional and shows real security intent (it SHA256-audits native binaries at build time, scrubs the keystore in CI, and scopes most broadcasts). But it carries debt on four fronts that scale badly toward the README's "millions of users" goal: @@ -96,4 +115,4 @@ Begin field work immediately with this batch — all low-effort, high-signal, an 4. **First tests:** unit-test the pure functions listed in Phase 0 to seed the safety net (`M10`). 5. **Config extraction (start):** move scattered URLs/ports/versions into one constants class (`D3`, `S7`) — mechanical, unblocks later phases. -After this batch lands, open the Phase 1 security epic with `D2`, `D6`, and `S1` as the first three tickets. +After this \ No newline at end of file diff --git a/controller/termux-core/termux-source b/controller/termux-core/termux-source index f8f3661..30ebb2d 160000 --- a/controller/termux-core/termux-source +++ b/controller/termux-core/termux-source @@ -1 +1 @@ -Subproject commit f8f3661432ccd316ddee6e08811c9e28683cf4d2 +Subproject commit 30ebb2dee381d292ade0f2868cfde0f9f20b89fe