Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* ============================================================================
* Name : DashNodeRequirement.java
* Author : AppDevForAll
* Copyright : Copyright (c) 2026 AppDevForAll
* Description : K2GO-430. Per-module minimum dash-node version. Pure data + lookup; the version compare
* and the box read live elsewhere (redesign.DashboardVersion), so this stays a plain,
* unit-testable registry with no Android or network dependency.
*
* Forgejo needs the dashboard build that ships its repo status/refresh endpoints (1.3.7):
* an older dash-node makes "Install repos"/"Update repos" and the seed break silently, so
* installing Forgejo is HARD-blocked below that version. Other modules have no hard minimum
* (a soft "update the dashboard" suggestion applies to all installs, handled in the gate).
* ============================================================================
*/
package org.appdevforall.k2go.dependency.domain;

import androidx.annotation.Nullable;

public final class DashNodeRequirement {
private DashNodeRequirement() {}

/** {major, minor, patch} minimum dash-node for {@code moduleKey}, or null when it has no hard minimum. */
@Nullable
public static int[] minFor(@Nullable String moduleKey) {
if ("forgejo".equals(moduleKey)) return new int[]{1, 3, 7};
return null;
}

/** Human "x.y.z" label for the minimum, or null when there is none. */
@Nullable
public static String minLabel(@Nullable String moduleKey) {
int[] m = minFor(moduleKey);
return m == null ? null : m[0] + "." + m[1] + "." + m[2];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* ============================================================================
* Name : DashNodeGate.java
* Author : AppDevForAll
* Copyright : Copyright (c) 2026 AppDevForAll
* Description : K2GO-430. Gate a module install on the box dash-node version. Two mechanisms:
* HARD block : a module that declares a minimum (DashNodeRequirement, e.g. Forgejo -> 1.3.7)
* cannot install on a box below it; show a dialog and route to the dashboard update.
* SOFT suggest : for a batch, when a dashboard update is available, recommend updating first
* (Update / Install anyway). Reuses the existing version read (DashboardVersion, from the
* rootfs, no network/proot) and the cached update-check (UpdateStatusCache), so the gate is
* synchronous.
* ============================================================================
*/
package org.appdevforall.k2go.dependency.presentation;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import org.appdevforall.k2go.R;
import org.appdevforall.k2go.dependency.domain.DashNodeRequirement;
import org.appdevforall.k2go.redesign.DashboardVersion;
import org.appdevforall.k2go.redesign.OpReturnNavigator;
import org.appdevforall.k2go.redesign.UpdateStatusCache;
import org.appdevforall.k2go.ui.dialog.BrandDialog;

public final class DashNodeGate {
private DashNodeGate() {}

/** True when the box dash-node is below {@code moduleKey}'s hard minimum (null key / no minimum = false).
* A null (unreadable) installed version blocks a module that HAS a minimum: do not install it against
* a dashboard we cannot verify. */
public static boolean installBlocked(@NonNull Context ctx, @Nullable String moduleKey) {
int[] min = DashNodeRequirement.minFor(moduleKey);
return min != null && !DashboardVersion.atLeast(DashboardVersion.installed(ctx), min[0], min[1], min[2]);
}

/** The first key below its dash-node minimum, or null. */
@Nullable
private static String firstBlocked(@NonNull Context ctx, @Nullable String[] keys) {
if (keys == null) return null;
for (String k : keys) if (installBlocked(ctx, k)) return k;
return null;
}

/**
* Gate a module install batch before it runs. If any banked module needs a newer dash-node than the
* box has, block and route to the dashboard update. Otherwise, if a dashboard update is available,
* suggest it first (Update / Install anyway). Otherwise run {@code onProceed} straight away.
*/
public static void guardModuleBatch(@NonNull Activity act, @Nullable String[] bankedKeys, @NonNull Runnable onProceed) {
String blocked = firstBlocked(act, bankedKeys);
if (blocked != null) { showBlocked(act, blocked); return; }
if (UpdateStatusCache.updateAvailable(act)) { showSuggest(act, onProceed); return; }
onProceed.run();
}

/**
* Gate a single-module (re)install, e.g. a retry: returns false (and shows the block dialog) when the
* module is below its dash-node minimum, true when it may proceed. Hard block only (a retry does not
* carry the soft update suggestion). Covers the install paths that start the index directly, not via
* the batch gate.
*/
public static boolean allowInstall(@NonNull Context ctx, @Nullable String moduleKey) {
if (installBlocked(ctx, moduleKey)) { showBlocked(ctx, moduleKey); return false; }
return true;
}

private static void showBlocked(@NonNull Context ctx, @NonNull String moduleKey) {
new BrandDialog(ctx)
.setTitle(R.string.k2go_dep_required_title)
.setMessage(ctx.getString(R.string.k2go_dep_required_msg_fmt, DashNodeRequirement.minLabel(moduleKey)))
.setPositive(R.string.k2go_dep_update_dashboard, BrandDialog.Role.PRIMARY, () -> openDashboard(ctx))
.setNegative(android.R.string.cancel, null)
.show();
}

private static void showSuggest(@NonNull Context ctx, @NonNull Runnable onProceed) {
new BrandDialog(ctx)
.setTitle(R.string.k2go_dep_suggest_title)
.setMessage(R.string.k2go_dep_suggest_msg)
.setPositive(R.string.k2go_dep_update_dashboard, BrandDialog.Role.PRIMARY, () -> openDashboard(ctx))
.setNegative(R.string.k2go_dep_install_anyway, onProceed::run)
// The suggestion is optional: dismissing it (back / tap-outside) proceeds with the install
// the user already asked for, rather than silently swallowing it.
.setOnCancel(onProceed::run)
.show();
}

/** Open the dashboard detail (the Rebuild/Update card) so the user can update the dash-node. Reuses the
* canonical navigation intent instead of rebuilding it here. */
private static void openDashboard(@NonNull Context ctx) {
Intent i = OpReturnNavigator.dashboardRebuild(ctx);
if (!(ctx instanceof Activity)) i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ctx.startActivity(i);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ public static boolean fire(View anchor, String moduleKey) {
Snackbars.make(anchor, BusyMessage.resFor(ctx)).show();
return false;
}
// K2GO-430: a retry starts the install directly (not via openModuleIndex), so gate the dash-node
// dependency here too. If the box dash-node is below the module's minimum, the gate shows the
// "update the dashboard first" dialog and the retry does not fire.
if (!org.appdevforall.k2go.dependency.presentation.DashNodeGate.allowInstall(ctx, moduleKey)) {
return false;
}
InstallService.retryModules(ctx, Collections.singletonList(moduleKey));
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -659,17 +659,23 @@
* banked in ModuleWishlist; the index drains them through the proot queue (ModuleProvisioner),
* same mechanism as maps. */
public void openModuleIndex() {
// ADFA-5228: module app installs are proot (STOPPED) runroles — confirm before entering the
// index that drains them. Class is read from the operation (the first banked module); a LIVE
// op would pass straight through the gate.
String[] banked = ModuleWishlist.keys(this);
org.appdevforall.k2go.system.domain.Operation op =
org.appdevforall.k2go.system.domain.Operation.appInstall(banked.length > 0 ? banked[0] : "");
// K2GO-404: the runrole fetches over the network inside the proot, so gate on metered cost at
// the START (like the rootfs/maps installs); a decline leaves the modules banked.
InstallConfirm.gate(this, op, () ->
org.appdevforall.k2go.networkpolicy.presentation.NetworkPolicyGate.guardHeavyStart(this,
() -> startActivity(new Intent(this, SetupProgressActivity.class))));
final String[] banked = ModuleWishlist.keys(this);
// K2GO-430: a banked module may need a newer dash-node than the box has (Forgejo -> 1.3.7). Block
// the batch and route to the dashboard update when any module is below its minimum; else, when a
// dashboard update is available, suggest it first. This gates every install path (they all run
// through here) before the proot queue starts.
org.appdevforall.k2go.dependency.presentation.DashNodeGate.guardModuleBatch(this, banked, () -> {
// ADFA-5228: module app installs are proot (STOPPED) runroles — confirm before entering the
// index that drains them. Class is read from the operation (the first banked module); a LIVE
// op would pass straight through the gate.
org.appdevforall.k2go.system.domain.Operation op =
org.appdevforall.k2go.system.domain.Operation.appInstall(banked.length > 0 ? banked[0] : "");
// K2GO-404: the runrole fetches over the network inside the proot, so gate on metered cost at
// the START (like the rootfs/maps installs); a decline leaves the modules banked.
InstallConfirm.gate(this, op, () ->
org.appdevforall.k2go.networkpolicy.presentation.NetworkPolicyGate.guardHeavyStart(this,
() -> startActivity(new Intent(this, SetupProgressActivity.class))));
});
}

/** ADFA-4952: open the dedicated backup/restore job screen (mode = MODE_BACKUP / MODE_RESTORE). */
Expand All @@ -685,7 +691,7 @@
* on purpose (not deleted): ADFA-4842 (reactivate proot modules via module management) may want
* to generalize a "install these proot modules now" path from here — 4842 decides whether to
* reuse/generalize or delete. Deleting now could remove something module management wants. */
public void openMapsPreparing(String[] levels) {

Check warning on line 694 in controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java

View workflow job for this annotation

GitHub Actions / Build & Distribute

[dep-ann*** deprecated item is not annotated with @deprecated

Check warning on line 694 in controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java

View workflow job for this annotation

GitHub Actions / Lint & Compile Check

[dep-ann] deprecated item is not annotated with @deprecated

Check warning on line 694 in controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java

View workflow job for this annotation

GitHub Actions / Lint & Compile Check

[dep-ann] deprecated item is not annotated with @deprecated
getSupportFragmentManager().beginTransaction()
.replace(R.id.k2go_setup_host, MapsPreparingFragment.newInstance(levels))
.addToBackStack("maps_preparing")
Expand Down Expand Up @@ -734,7 +740,7 @@
* the per-layer selection). InstallService writes the full maps_* local_vars and runs runrole
* with the shared success/failure verdict, revert-on-fail and observable progress. {@code levels}
* is aligned to the Choose groups [base, satellite, terrain, search]; null = off. */
public void startMapsInstall(String[] levels) {

Check warning on line 743 in controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java

View workflow job for this annotation

GitHub Actions / Build & Distribute

[dep-ann*** deprecated item is not annotated with @deprecated

Check warning on line 743 in controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java

View workflow job for this annotation

GitHub Actions / Lint & Compile Check

[dep-ann] deprecated item is not annotated with @deprecated

Check warning on line 743 in controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java

View workflow job for this annotation

GitHub Actions / Lint & Compile Check

[dep-ann] deprecated item is not annotated with @deprecated
String base = levels != null && levels.length > 0 && levels[0] != null ? levels[0] : "11";
String sat = levels != null && levels.length > 1 && levels[1] != null ? levels[1] : "none";
String ter = levels != null && levels.length > 2 && levels[2] != null ? levels[2] : "0-none";
Expand All @@ -756,7 +762,7 @@
* backToGetMoreHubZim, along with the ZIM live door that was the only thing calling it.)
* ADFA-4848: "Run in background" from Preparing -> drop the whole Maps flow off the back
* stack and return to the Get More hub; the build keeps running. */
public void backToGetMoreHub() {

Check warning on line 765 in controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java

View workflow job for this annotation

GitHub Actions / Build & Distribute

[dep-ann*** deprecated item is not annotated with @deprecated

Check warning on line 765 in controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java

View workflow job for this annotation

GitHub Actions / Lint & Compile Check

[dep-ann] deprecated item is not annotated with @deprecated

Check warning on line 765 in controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java

View workflow job for this annotation

GitHub Actions / Lint & Compile Check

[dep-ann] deprecated item is not annotated with @deprecated
getSupportFragmentManager().popBackStack("getmore_maps",
androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
Expand Down
6 changes: 6 additions & 0 deletions controller/app/src/main/res/values-ar/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1370,4 +1370,10 @@
<string name="k2go_forgejo_update_failed">تعذّر تحديث المستودعات</string>
<string name="k2go_forgejo_update_cancelling">جارٍ الإلغاء...</string>
<string name="k2go_forgejo_update_cancelled">تم إلغاء التحديث</string>
<string name="k2go_dep_required_title">حدّث dashboard أولاً</string>
<string name="k2go_dep_required_msg_fmt">تحتاج هذه الوحدة إلى dashboard بالإصدار %1$s أو أحدث قبل تثبيتها.</string>
<string name="k2go_dep_update_dashboard">تحديث dashboard</string>
<string name="k2go_dep_suggest_title">يتوفر تحديث لـ dashboard</string>
<string name="k2go_dep_suggest_msg">يُوصى بتحديث dashboard أولاً حتى تعمل ميزات الوحدة الجديدة بشكل صحيح.</string>
<string name="k2go_dep_install_anyway">التثبيت على أي حال</string>
</resources>
6 changes: 6 additions & 0 deletions controller/app/src/main/res/values-az/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1390,4 +1390,10 @@
<string name="k2go_forgejo_update_failed">Anbarlar yenilene bilmedi</string>
<string name="k2go_forgejo_update_cancelling">Legv edilir...</string>
<string name="k2go_forgejo_update_cancelled">Yenileme legv edildi</string>
<string name="k2go_dep_required_title">Evvelce dashboard-u yenileyin</string>
<string name="k2go_dep_required_msg_fmt">Bu modul qurasdirila bilmesi ucun %1$s ve ya daha yeni dashboard versiyasina ehtiyac duyur.</string>
<string name="k2go_dep_update_dashboard">Dashboard-u yenile</string>
<string name="k2go_dep_suggest_title">Dashboard yenilemesi movcuddur</string>
<string name="k2go_dep_suggest_msg">Yeni modul funksiyalarinin duzgun islemesi ucun evvelce dashboard-u yenilemek tovsiye olunur.</string>
<string name="k2go_dep_install_anyway">Yene de qurasdir</string>
</resources>
6 changes: 6 additions & 0 deletions controller/app/src/main/res/values-bg/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1377,4 +1377,10 @@
<string name="k2go_forgejo_update_failed">Хранилищата не можаха да бъдат обновени</string>
<string name="k2go_forgejo_update_cancelling">Отказване...</string>
<string name="k2go_forgejo_update_cancelled">Обновяването е отказано</string>
<string name="k2go_dep_required_title">Първо обновете dashboard</string>
<string name="k2go_dep_required_msg_fmt">Този модул се нуждае от dashboard версия %1$s или по-нова, преди да може да се инсталира.</string>
<string name="k2go_dep_update_dashboard">Обнови dashboard</string>
<string name="k2go_dep_suggest_title">Налична е актуализация на dashboard</string>
<string name="k2go_dep_suggest_msg">Препоръчва се първо да обновите dashboard, за да работят правилно новите функции на модула.</string>
<string name="k2go_dep_install_anyway">Инсталирай въпреки това</string>
</resources>
6 changes: 6 additions & 0 deletions controller/app/src/main/res/values-bn/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1383,4 +1383,10 @@
<string name="k2go_forgejo_update_failed">রিপোজিটরি আপডেট করা যায়নি</string>
<string name="k2go_forgejo_update_cancelling">বাতিল করা হচ্ছে...</string>
<string name="k2go_forgejo_update_cancelled">আপডেট বাতিল করা হয়েছে</string>
<string name="k2go_dep_required_title">প্রথমে dashboard আপডেট করুন</string>
<string name="k2go_dep_required_msg_fmt">ইনস্টল করার আগে এই মডিউলের dashboard %1$s বা নতুন সংস্করণ প্রয়োজন।</string>
<string name="k2go_dep_update_dashboard">dashboard আপডেট করুন</string>
<string name="k2go_dep_suggest_title">dashboard আপডেট উপলব্ধ</string>
<string name="k2go_dep_suggest_msg">মডিউলের নতুন বৈশিষ্ট্য ঠিকমতো কাজ করার জন্য প্রথমে dashboard আপডেট করার পরামর্শ দেওয়া হয়।</string>
<string name="k2go_dep_install_anyway">তবুও ইনস্টল করুন</string>
</resources>
6 changes: 6 additions & 0 deletions controller/app/src/main/res/values-cs/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1377,4 +1377,10 @@
<string name="k2go_forgejo_update_failed">Repozitare se nepodarilo aktualizovat</string>
<string name="k2go_forgejo_update_cancelling">Ruseni...</string>
<string name="k2go_forgejo_update_cancelled">Aktualizace zrusena</string>
<string name="k2go_dep_required_title">Nejprve aktualizujte dashboard</string>
<string name="k2go_dep_required_msg_fmt">Tento modul potrebuje dashboard ve verzi %1$s nebo novejsi, nez jej bude mozne nainstalovat.</string>
<string name="k2go_dep_update_dashboard">Aktualizovat dashboard</string>
<string name="k2go_dep_suggest_title">Je dostupna aktualizace dashboardu</string>
<string name="k2go_dep_suggest_msg">Doporucujeme nejprve aktualizovat dashboard, aby nove funkce modulu fungovaly spravne.</string>
<string name="k2go_dep_install_anyway">Presto nainstalovat</string>
</resources>
6 changes: 6 additions & 0 deletions controller/app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1370,4 +1370,10 @@
<string name="k2go_forgejo_update_failed">Repositorys konnten nicht aktualisiert werden</string>
<string name="k2go_forgejo_update_cancelling">Wird abgebrochen...</string>
<string name="k2go_forgejo_update_cancelled">Aktualisierung abgebrochen</string>
<string name="k2go_dep_required_title">Zuerst das Dashboard aktualisieren</string>
<string name="k2go_dep_required_msg_fmt">Dieses Modul benoetigt das Dashboard in Version %1$s oder neuer, bevor es installiert werden kann.</string>
<string name="k2go_dep_update_dashboard">Dashboard aktualisieren</string>
<string name="k2go_dep_suggest_title">Ein Dashboard-Update ist verfuegbar</string>
<string name="k2go_dep_suggest_msg">Es wird empfohlen, zuerst das Dashboard zu aktualisieren, damit neue Modulfunktionen richtig laufen.</string>
<string name="k2go_dep_install_anyway">Trotzdem installieren</string>
</resources>
6 changes: 6 additions & 0 deletions controller/app/src/main/res/values-el/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1377,4 +1377,10 @@
<string name="k2go_forgejo_update_failed">Δεν ήταν δυνατή η ενημέρωση των αποθετηρίων</string>
<string name="k2go_forgejo_update_cancelling">Ακύρωση...</string>
<string name="k2go_forgejo_update_cancelled">Η ενημέρωση ακυρώθηκε</string>
<string name="k2go_dep_required_title">Ενημερώστε πρώτα το dashboard</string>
<string name="k2go_dep_required_msg_fmt">Αυτή η μονάδα χρειάζεται το dashboard στην έκδοση %1$s ή νεότερη πριν εγκατασταθεί.</string>
<string name="k2go_dep_update_dashboard">Ενημέρωση dashboard</string>
<string name="k2go_dep_suggest_title">Διαθέσιμη ενημέρωση dashboard</string>
<string name="k2go_dep_suggest_msg">Συνιστάται να ενημερώσετε πρώτα το dashboard ώστε οι νέες λειτουργίες της μονάδας να λειτουργούν σωστά.</string>
<string name="k2go_dep_install_anyway">Εγκατάσταση ούτως ή άλλως</string>
</resources>
6 changes: 6 additions & 0 deletions controller/app/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1446,4 +1446,10 @@
<string name="k2go_forgejo_update_failed">No se pudieron actualizar los repositorios</string>
<string name="k2go_forgejo_update_cancelling">Cancelando...</string>
<string name="k2go_forgejo_update_cancelled">Actualizacion cancelada</string>
<string name="k2go_dep_required_title">Actualiza primero el dashboard</string>
<string name="k2go_dep_required_msg_fmt">Este modulo necesita el dashboard actualizado a la version %1$s o mas reciente antes de poder instalarse.</string>
<string name="k2go_dep_update_dashboard">Actualizar dashboard</string>
<string name="k2go_dep_suggest_title">Hay una actualizacion del dashboard disponible</string>
<string name="k2go_dep_suggest_msg">Se recomienda actualizar primero el dashboard para que las nuevas funciones del modulo funcionen bien.</string>
<string name="k2go_dep_install_anyway">Instalar de todos modos</string>
</resources>
6 changes: 6 additions & 0 deletions controller/app/src/main/res/values-fa/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1370,4 +1370,10 @@
<string name="k2go_forgejo_update_failed">به‌روزرسانی مخازن ممکن نشد</string>
<string name="k2go_forgejo_update_cancelling">در حال لغو...</string>
<string name="k2go_forgejo_update_cancelled">به‌روزرسانی لغو شد</string>
<string name="k2go_dep_required_title">ابتدا dashboard را به‌روزرسانی کنید</string>
<string name="k2go_dep_required_msg_fmt">این ماژول پیش از نصب به dashboard نسخه %1$s یا جدیدتر نیاز دارد.</string>
<string name="k2go_dep_update_dashboard">به‌روزرسانی dashboard</string>
<string name="k2go_dep_suggest_title">به‌روزرسانی dashboard در دسترس است</string>
<string name="k2go_dep_suggest_msg">توصیه می‌شود ابتدا dashboard را به‌روزرسانی کنید تا قابلیت‌های جدید ماژول درست کار کنند.</string>
<string name="k2go_dep_install_anyway">به هر حال نصب شود</string>
</resources>
Loading
Loading