Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
ae1377a
ADFA-4725: new UI shell — scoped M3 theme + LibraryActivity + bottom …
luisguzman-adfa Jul 17, 2026
7c2006e
ADFA-4725: runtime boot gate — own ServerController.Host, auto-start,…
luisguzman-adfa Jul 17, 2026
e3cfcea
ADFA-4725: content-first Library home — 5 cards, 3-state status dots,…
luisguzman-adfa Jul 17, 2026
cba7b9d
Merge main (ADFA-4746 green connected tests) into redesign/ui
luisguzman-adfa Jul 17, 2026
945199a
ADFA-4725: route first-run wizard to the new UI — LibraryActivity set…
luisguzman-adfa Jul 17, 2026
187040e
ADFA-4725: fix lint UseAppTint — app:tint on the Library card icon (P…
luisguzman-adfa Jul 17, 2026
d493426
ADFA-4725: content language follows the system + normalize legacy ISO…
luisguzman-adfa Jul 17, 2026
b57e580
ADFA-4725: Set up your library — Step 1 System (editions + live stora…
luisguzman-adfa Jul 17, 2026
1ea0692
ADFA-4725: fix Step 1 projection listener (PlanResultListener is not …
luisguzman-adfa Jul 17, 2026
ef9d165
ADFA-4725: Step 2 Content Option A (expandable + storage bar, real Ki…
luisguzman-adfa Jul 17, 2026
853df56
ADFA-4725: fix Step 2 catalog listener (CacheListener is not a SAM) (…
luisguzman-adfa Jul 17, 2026
ea05f47
ADFA-4725: Step 2 Option B (5-step + gauge) + hidden tap-5x A/B switc…
luisguzman-adfa Jul 17, 2026
afcc95f
ADFA-4725: Settings tab (About live + Advanced rows) + Turn off K2Go …
luisguzman-adfa Jul 17, 2026
7902eaa
ADFA-4725: Connect + Clone designed shells (Phase 6.2)
luisguzman-adfa Jul 17, 2026
3f1d83a
ADFA-4725: polish — card 'Unavailable · tap to retry' + reduce-motion…
luisguzman-adfa Jul 17, 2026
7d4ad7f
ADFA-4725: new first-run onboarding (Welcome/Language/Permissions/Set…
luisguzman-adfa Jul 17, 2026
4e064a7
ADFA-4725: use the official K2Go logo (splash_logo) in the new onboar…
luisguzman-adfa Jul 17, 2026
26689c6
ADFA-4725: extract redesign layout strings to strings_k2go.xml (Engli…
luisguzman-adfa Jul 17, 2026
199422c
ADFA-4725/overlay: replace SAW background bring-to-front with a tap-t…
luisguzman-adfa Jul 17, 2026
4071609
ADFA-4725: pin the onboarding CTA to the bottom (consistent across st…
luisguzman-adfa Jul 17, 2026
9076d40
ADFA-4725: onboarding Welcome — logo on top, title+subtitle centered …
luisguzman-adfa Jul 17, 2026
28d36ac
ADFA-4725: onboarding Welcome — nudge the logo/title block down (top …
luisguzman-adfa Jul 17, 2026
dcb06b1
ADFA-4725: onboarding — add the '< Back' link on Language/Permissions…
luisguzman-adfa Jul 17, 2026
5a128bb
ADFA-4725: fix invisible storage bars (setBackgroundColor) + Step 1 '…
luisguzman-adfa Jul 17, 2026
3b5169f
ADFA-4725: fix crash on Library open — feed Lottie the app Atkinson f…
luisguzman-adfa Jul 17, 2026
0a75594
ADFA-4725: Step 2 (A & B) — bottom-anchored primary CTA + '< Back' li…
luisguzman-adfa Jul 17, 2026
8fcd751
ADFA-4725: Step 2 Option B — stack Next (full-width) + '< Back' link …
luisguzman-adfa Jul 17, 2026
3d7ec94
ADFA-4725: Step 2 Option A per design — collapsible Wikipedia/Books/M…
luisguzman-adfa Jul 17, 2026
628ec78
ADFA-4725: Step 2 Option B uses the arc gauge (reuse MultiResourceGau…
luisguzman-adfa Jul 17, 2026
20a3001
ADFA-4725: fix Step 2 Option B fragment to use the arc gauge (removes…
luisguzman-adfa Jul 17, 2026
f891924
ADFA-4725: Step 2 fixes — Books 0 GB (coming soon), Step-2 breadcrumb…
luisguzman-adfa Jul 17, 2026
1f42a38
ADFA-4725: draw (i) info badges in Step 2 Option A (Wikipedia/Books/M…
luisguzman-adfa Jul 17, 2026
68a47bb
ADFA-4725: Step 2 A/B — drop the weighted spacer (Download follows co…
luisguzman-adfa Jul 17, 2026
b92c1ad
ADFA-4725: Step 2 A/B — remove fillViewport (ScrollView measured natu…
luisguzman-adfa Jul 17, 2026
4709644
ADFA-4725: Step 2 Option B — Include-Wikipedia checkbox + per-step Sk…
luisguzman-adfa Jul 17, 2026
87359c3
ADFA-4725: add 'Skip for now' — wizard step 4 + Step 1 System open th…
luisguzman-adfa Jul 17, 2026
588426f
ADFA-4725: Wikipedia install honors the UI pick (no silent "Everythin…
luisguzman-adfa Jul 17, 2026
dcfeb96
ADFA-4725: Wikipedia version picker — List/Grouped, multi-select (min…
luisguzman-adfa Jul 17, 2026
5102c05
ADFA-4725: Step 1 edition sizes are real, not hardcoded
luisguzman-adfa Jul 17, 2026
744609a
ADFA-4725: remove dead-end feel — persistent Portal exit, wired/previ…
luisguzman-adfa Jul 17, 2026
439f2cf
ADFA-4725: Step 1 'Back' returns to the library, not the Android home
luisguzman-adfa Jul 17, 2026
a703390
Merge remote-tracking branch 'origin/main' into redesign/ui
luisguzman-adfa Jul 17, 2026
b0bcf2b
ADFA-4725: Settings v3 — collapsed list, sub-screens, pinned Turn off…
luisguzman-adfa Jul 17, 2026
361bde7
ADFA-4725: fix doubled 'v' in Settings app version (versionName alrea…
luisguzman-adfa Jul 17, 2026
dda6a95
ADFA-4725: wire Advanced > Terminal (Debian) to the existing full ter…
luisguzman-adfa Jul 17, 2026
80d95fd
ADFA-4725: terminal opens in "terminal-only" mode — no leak into the …
luisguzman-adfa Jul 17, 2026
37d8c66
ADFA-4725: terminal keep-alive notification also opens terminal-only
luisguzman-adfa Jul 17, 2026
62d8b27
ADFA-4725: non-destructive "Get more" — never overwrite an installed …
luisguzman-adfa Jul 18, 2026
5f4c68b
ADFA-4725: skipping Wikipedia means zero — and align Option B breadcrumb
luisguzman-adfa Jul 18, 2026
771c503
ADFA-4725: Settings toggle row no longer oversized (cap the switch he…
luisguzman-adfa Jul 18, 2026
d581ab0
ADFA-4725: Option A — Wikipedia starts collapsed like the other sources
luisguzman-adfa Jul 18, 2026
702ef71
ADFA-4725: Library — pin '+ Get more' as a footer so it never scrolls…
luisguzman-adfa Jul 18, 2026
a5bcf0f
ADFA-4725: short boot gate when nothing is installed (no system to wa…
luisguzman-adfa Jul 18, 2026
2a27d8a
ADFA-4725: entry gate waits for the install and shows real progress
luisguzman-adfa Jul 18, 2026
3f70d8b
ADFA-4725: Turn off K2Go frame is clay, matching its text (not the te…
luisguzman-adfa Jul 18, 2026
26c0e81
ADFA-4725: drop the redundant 'Downloading your library' toast on Dow…
luisguzman-adfa Jul 18, 2026
6f09a43
ADFA-4725: disable Maps install in the content flow (it ships with th…
luisguzman-adfa Jul 18, 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
1 change: 1 addition & 0 deletions controller/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ dependencies {
// 10. Core dependencies
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.android.material:material:1.13.0'
implementation 'com.airbnb.android:lottie:6.1.0'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'androidx.biometric:biometric:1.1.0'
implementation 'androidx.webkit:webkit:1.12.0'
Expand Down
20 changes: 19 additions & 1 deletion controller/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
Expand Down Expand Up @@ -80,6 +79,25 @@
tools:replace="screenOrientation" />

<activity android:name=".SetupActivity" />

<!-- ADFA-4725: new content-first UI shell (scoped Material 3 theme). -->
<activity
android:name=".redesign.LibraryActivity"
android:exported="false"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:theme="@style/Theme.K2Go" />

<activity
android:name=".redesign.SetupLibraryActivity"
android:exported="false"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:theme="@style/Theme.K2Go" />

<activity
android:name=".redesign.WizardActivity"
android:exported="false"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:theme="@style/Theme.K2Go" />
<activity android:name=".feedback.crash.presentation.CrashReportActivity" />
<activity android:name=".QrActivity" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class AdbPairingReceiver extends BroadcastReceiver {

public static final String KEY_PIN_REPLY = "key_pin_reply";
public static final int NOTIFICATION_ID = 9401;
public static final int RETURN_NOTIFICATION_ID = 9402;
private static final String TAG = "AdbPairingNative";


Expand Down Expand Up @@ -73,10 +74,25 @@ private void performNativePairing(Context context, String hostIp, int pairingPor
.putBoolean("pairing_just_succeeded", true)
.apply();

// CRITICAL: Bring our app back to the screen automatically!
Intent bringToFront = new Intent(context, MainActivity.class);
bringToFront.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(bringToFront);
// ADFA-4725 (overlay decision, Option B): no SAW / no background
// startActivity. Post an OS-sanctioned "tap to return" notification that
// opens the new UI (LibraryActivity). Reliable on restrictive phones.
Intent open = new Intent(context, org.iiab.controller.redesign.LibraryActivity.class);
open.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
int piFlags = android.app.PendingIntent.FLAG_UPDATE_CURRENT
| (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M
? android.app.PendingIntent.FLAG_IMMUTABLE : 0);
android.app.PendingIntent pi = android.app.PendingIntent.getActivity(context, 0, open, piFlags);
NotificationManager nmReturn = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (nmReturn != null) {
NotificationCompat.Builder rb = new NotificationCompat.Builder(context, "adb_pairing_channel")
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle("Paired")
.setContentText("Tap to return to K2Go")
.setAutoCancel(true)
.setContentIntent(pi);
nmReturn.notify(RETURN_NOTIFICATION_ID, rb.build());
}

} else {
Log.e(TAG, "Native pairing failed.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,54 @@ public static void wipeCache(Context context) {
if (cacheFile.exists()) cacheFile.delete();
}

/** Canonical Wikipedia variants, ordered coverage-desc then detail-desc. */
public static final String[] CANONICAL_VARIANTS = {
"all_maxi", "all_nopic", "all_mini",
"top1m_maxi", "top1m_nopic", "top1m_mini",
"top_maxi", "top_nopic", "top_mini"
};

/** Variants actually present for a language, in canonical order (for the UI to render). */
public static java.util.List<String> availableVariants(JSONObject langData) {
java.util.List<String> out = new java.util.ArrayList<>();
if (langData == null) return out;
for (String key : CANONICAL_VARIANTS) if (langData.has(key)) out.add(key);
return out;
}

private static String coverageOf(String variant) {
if (variant.startsWith("all_")) return "all";
if (variant.startsWith("top1m_")) return "top1m";
return "top";
}
private static String detailOf(String variant) {
if (variant.endsWith("_maxi")) return "maxi";
if (variant.endsWith("_mini")) return "mini";
return "nopic";
}

/**
* Resolve an explicit variant against what the language actually offers, degrading only
* DOWNWARD within the coverage family (top1m -> top) and across detail, never escalating.
* Returns a present key or null.
*/
private static String resolveInFamily(JSONObject langData, String requested) {
if (langData == null) return null;
if (langData.has(requested)) return requested;
String cov = coverageOf(requested);
String det = detailOf(requested);
String[] covLadder = cov.equals("all") ? new String[]{"all"}
: cov.equals("top1m") ? new String[]{"top1m", "top"}
: new String[]{"top"};
String[] detLadder = det.equals("maxi") ? new String[]{"maxi", "nopic", "mini"}
: det.equals("nopic") ? new String[]{"nopic", "mini", "maxi"}
: new String[]{"mini", "nopic", "maxi"};
for (String cc : covLadder)
for (String dd : detLadder)
if (langData.has(cc + "_" + dd)) return cc + "_" + dd;
return null;
}

public static void getOrFetchCatalog(Context context, CacheListener listener) {
new Thread(() -> {
File cacheFile = new File(context.getCacheDir(), CACHE_FILE_NAME);
Expand Down Expand Up @@ -134,12 +182,19 @@ public static void getOrFetchCatalog(Context context, CacheListener listener) {

if (!database.has(lang)) database.put(lang, new JSONObject());

// NEW: Store size AND exact filename
JSONObject variantData = new JSONObject();
variantData.put("size", sizeGb);
variantData.put("file", "wikipedia_" + lang + "_" + variant + "_" + date + ".zim");

database.getJSONObject(lang).put(variant, variantData);
// Store size, exact filename AND date. The listing can hold more than
// one date for the same variant; keep ONLY the newest by declared
// intent ("YYYY-MM" compares lexicographically = chronologically),
// not by whichever row happens to come last in the HTML.
JSONObject langObj = database.getJSONObject(lang);
JSONObject existing = langObj.optJSONObject(variant);
if (existing == null || date.compareTo(existing.optString("date", "")) > 0) {
JSONObject variantData = new JSONObject();
variantData.put("size", sizeGb);
variantData.put("date", date);
variantData.put("file", "wikipedia_" + lang + "_" + variant + "_" + date + ".zim");
langObj.put(variant, variantData);
}
}
}

Expand Down Expand Up @@ -223,30 +278,40 @@ private static void computeProjection(Context context, Tier tier, boolean pullCo
@Override
public void onReady(JSONObject catalog) {
double kiwixSize = 0.0;
String resolvedLang = langCode;
String resolvedLang = org.iiab.controller.applang.data.ContentLanguage.normalize(langCode);
String resolvedFile = null;

JSONObject langData = catalog.optJSONObject(langCode);
JSONObject langData = catalog.optJSONObject(resolvedLang);

if (langData == null) {
langData = catalog.optJSONObject("en");
resolvedLang = "en";
}

if (langData != null) {
if (overrideVariant != null && langData.has(overrideVariant)) {
JSONObject vData = langData.optJSONObject(overrideVariant);
// VDATA NULL PROTECTION
if (vData != null) {
kiwixSize = vData.optDouble("size", 0.0);
resolvedFile = vData.optString("file", null);
if (overrideVariant != null && overrideVariant.isEmpty()) {
// Explicit "no Wikipedia" (skip): leave kiwixSize 0 / resolvedFile
// null. Skipping must mean ZERO — never a tier-coupled auto-pick.
} else if (overrideVariant != null) {
// Explicit pick from the UI. Honor it exactly, or degrade WITHIN
// the same coverage family (e.g. top1m_maxi -> top_maxi), NEVER
// escalating — a "Popular" pick must not silently become the full
// "Everything" ZIM. If nothing in-family exists, install no
// Wikipedia rather than substituting a different corpus.
String resolved = resolveInFamily(langData, overrideVariant);
if (resolved != null) {
JSONObject vData = langData.optJSONObject(resolved);
if (vData != null) {
kiwixSize = vData.optDouble("size", 0.0);
resolvedFile = vData.optString("file", null);
}
}
} else {
// Legacy callers with no explicit pick: priority auto-select.
String[] priorities = {"all_maxi", "top1m_maxi", "all_nopic", "top1m_nopic", "top_maxi", "all_mini", "top_nopic"};
for (String p : priorities) {
if (langData.has(p)) {
JSONObject vData = langData.optJSONObject(p);
// VDATA NULL PROTECTION
if (vData != null) {
double size = vData.optDouble("size", 0.0);
if (size <= finalLimit) {
Expand Down Expand Up @@ -280,6 +345,12 @@ public void onError(String error) {
* <p>Performs a (cached) network call; must run off the main thread — it does,
* because every caller is inside {@link #calculateProjectedSize}'s worker thread.
*/
/** No-network last-known OS size (RootfsCatalog fallback) — instant, for placeholders. */
public static double fallbackOsSizeGb(Tier tier) {
RootfsCatalog catalog = new RootfsCatalog();
return ByteFormatter.toGiB(catalog.fallbackBytes(toDomainTier(tier), catalog.detectAbi()));
}

private static double resolveOsSizeGb(Context context, Tier tier) {
RootfsCatalog catalog = new RootfsCatalog(context);
RootfsRepository repository =
Expand Down
34 changes: 32 additions & 2 deletions controller/app/src/main/java/org/iiab/controller/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ public boolean isTermuxInstalled() {
/** Notification-tap flag: open the full terminal directly (ADFA-4696). */
public static final String EXTRA_OPEN_TERMINAL = "org.iiab.controller.OPEN_TERMINAL";

/** Launched only to show the terminal (from the new UI): finish on close instead of
* revealing the old dashboard. */
public static final String EXTRA_TERMINAL_ONLY = "org.iiab.controller.TERMINAL_ONLY";
private boolean terminalOnlyMode = false;

private TerminalController terminalController;

public void invalidateModuleStateTrust() {
Expand Down Expand Up @@ -581,10 +586,35 @@ protected void onNewIntent(Intent intent) {
private void maybeOpenTerminalFromIntent(Intent intent) {
if (intent == null || terminalController == null) return;
if (!intent.getBooleanExtra(EXTRA_OPEN_TERMINAL, false)) return;
terminalOnlyMode = intent.getBooleanExtra(EXTRA_TERMINAL_ONLY, false);
intent.removeExtra(EXTRA_OPEN_TERMINAL); // consume so it fires once
intent.removeExtra(EXTRA_TERMINAL_ONLY);
View root = findViewById(android.R.id.content);
if (root != null) root.post(() -> terminalController.openFullTerminal());
else terminalController.openFullTerminal();
Runnable open = () -> {
terminalController.openFullTerminal();
if (terminalOnlyMode) attachTerminalOnlyFinish();
};
if (root != null) root.post(open);
else open.run();
}

/** In terminal-only mode, hiding the terminal sheet returns to the caller (the new UI)
* rather than exposing the old dashboard behind it. */
private void attachTerminalOnlyFinish() {
View sheet = findViewById(R.id.terminal_bottom_sheet);
if (sheet == null) return;
com.google.android.material.bottomsheet.BottomSheetBehavior<View> b =
com.google.android.material.bottomsheet.BottomSheetBehavior.from(sheet);
b.addBottomSheetCallback(new com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@androidx.annotation.NonNull View bottomSheet, int newState) {
if (newState == com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN) {
finish();
}
}
@Override
public void onSlide(@androidx.annotation.NonNull View bottomSheet, float slideOffset) { }
});
}

private void toggleTheme() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,14 @@ protected void onCreate(Bundle savedInstanceState) {
Button btnExit = findViewById(R.id.btnExit);
Button btnForward = findViewById(R.id.btnForward);

// --- PREPARE HIDDEN BAR ---
// --- PERSISTENT BAR ---
// Keep the nav bar (with Exit) visible so a non-technical user always has an
// obvious way out. The handle is only used after a manual hide (btnHideNav).
bottomNav.post(() -> {
bottomNav.setTranslationY(bottomNav.getHeight()); // Move outside the screen
bottomNav.setTranslationY(0);
bottomNav.setVisibility(View.VISIBLE);
});
btnHandle.setVisibility(View.GONE);

// --- AUTO-HIDE TIMER ---
Handler hideHandler = new Handler(Looper.getMainLooper());
Expand All @@ -114,10 +117,8 @@ protected void onCreate(Bundle savedInstanceState) {
btnHandle.animate().alpha(1f).setDuration(150);
};

Runnable resetTimer = () -> {
hideHandler.removeCallbacks(hideRunnable);
hideHandler.postDelayed(hideRunnable, 5000);
};
// Persistent bar: never auto-hide. Cancel any pending hide; only btnHideNav hides.
Runnable resetTimer = () -> hideHandler.removeCallbacks(hideRunnable);

// --- HANDLE LOGIC (Show Bar) ---
btnHandle.setOnClickListener(v -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ private void completeSetup() {
SharedPreferences prefs = requireContext().getSharedPreferences(
getString(R.string.pref_file_internal), Context.MODE_PRIVATE);
prefs.edit().putBoolean(getString(R.string.pref_key_setup_complete), true).apply();
startActivity(new Intent(requireContext(), MainActivity.class));
startActivity(new Intent(requireContext(), org.iiab.controller.redesign.LibraryActivity.class));
requireActivity().finish();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ protected void onCreate(Bundle savedInstanceState) {
fade.setAlpha(0f);
new Handler(Looper.getMainLooper()).postDelayed(() ->
fade.animate().alpha(1f).setDuration(EXIT_FADE_MS).withEndAction(() -> {
startActivity(new Intent(SplashActivity.this, MainActivity.class));
startActivity(new Intent(SplashActivity.this, org.iiab.controller.redesign.LibraryActivity.class));
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
finish();
}).start(), EXIT_AT_MS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ private SystemStateEvaluator() {
private static volatile String cachedDebianArch;
private static volatile boolean archCalculated;

/** True when a system (rootfs) is actually installed on disk — the reliable signal for
* whether "Get more" should skip the destructive system step and go straight to content. */
public static boolean isSystemInstalled(Context ctx) {
File rootfsDir = new File(ctx.getFilesDir(), "rootfs/installed-rootfs/iiab");
return new File(rootfsDir, "bin/bash").exists()
|| new File(rootfsDir, "usr/local/pdsm/flag_install_ready").exists();
}

/** Server responding → ONLINE; else derive from the rootfs on disk. */
public static DashboardFragment.SystemState evaluate(Context ctx, boolean serverAlive) {
File rootfsDir = new File(ctx.getFilesDir(), "rootfs/installed-rootfs/iiab");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ private Notification buildNotification() {
// version-footer gesture (ADFA-4696). SINGLE_TOP reuses the running Activity.
Intent open = new Intent(this, MainActivity.class)
.putExtra(MainActivity.EXTRA_OPEN_TERMINAL, true)
.putExtra(MainActivity.EXTRA_TERMINAL_ONLY, true)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pending = PendingIntent.getActivity(
this, 0, open, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
Expand Down
Loading