@@ -132,7 +132,12 @@ protected void onCreate(Bundle savedInstanceState) {
132132 // ADFA-4915: extract detail is one middle-ellipsized line so long file names never overlap.
133133 installDetail .setMaxLines (1 );
134134 installDetail .setEllipsize (android .text .TextUtils .TruncateAt .MIDDLE );
135- installing = getIntent ().getBooleanExtra (EXTRA_INSTALLING , false );
135+ // ADFA-4986: also treat a live install as "installing" even when re-entered WITHOUT the extra
136+ // (tapping the install notification, or a relaunch). isRunning() covers DOWNLOADING/EXTRACTING/
137+ // PROVISIONING. Otherwise the gate takes the normal-boot path and its autostart/safety timers
138+ // start the server and OPEN over a system that is still provisioning -> a broken library.
139+ installing = getIntent ().getBooleanExtra (EXTRA_INSTALLING , false )
140+ || InstallProgressRepository .get ().current ().isRunning ();
136141 // The Lottie has a text layer (OPEN/CLOSED sign). Use the system typeface (Noto-based,
137142 // global script fallback) so localized words render in any language; a TextDelegate maps
138143 // the OPEN/CLOSED source text to the localized @string values.
@@ -288,16 +293,23 @@ public android.graphics.Typeface fetchFont(String fontFamily) {
288293 // If the stack isn't up after one poll cycle, start it.
289294 if (systemInstalled ) {
290295 main .postDelayed (() -> {
291- if (!isFinishing ()
296+ // ADFA-4986: never autostart the server if an install went live after onCreate.
297+ if (!isFinishing () && !installing
292298 && !ServerStateRepository .get ().current ().alive
293299 && targetServerState == null ) {
294300 serverController .handleServerLaunchClick (findViewById (android .R .id .content ));
295301 }
296302 }, AUTOSTART_DELAY_MS );
297303 }
298- // Safety: never trap the user behind the gate.
304+ // Safety: never trap the user behind the gate — but ADFA-4986: don't lift it mid-install.
305+ // Deliberate trade-off: while an install is live there is intentionally NO safety-timeout
306+ // dismissal here; the gate is lifted only when the install reaches a terminal state (the
307+ // InstallProgressRepository observer: SUCCESS starts the server then opens, FAILED opens
308+ // to the offline library) — the same contract as the first-run `if (installing)` branch,
309+ // which also has no safety net. A genuinely hung install (no terminal) is a separate
310+ // concern owned by the installer, not something to paper over by opening a broken system.
299311 main .postDelayed (() -> {
300- if (!gateDismissed ) {
312+ if (!gateDismissed && ! installing ) {
301313 onServerReady ();
302314 }
303315 }, systemInstalled ? GATE_SAFETY_MS : NO_SYSTEM_GATE_MS );
0 commit comments