diff --git a/controller/app/src/main/AndroidManifest.xml b/controller/app/src/main/AndroidManifest.xml
index 7e78799af..e741a75fd 100644
--- a/controller/app/src/main/AndroidManifest.xml
+++ b/controller/app/src/main/AndroidManifest.xml
@@ -154,6 +154,14 @@
android:exported="false"
android:foregroundServiceType="specialUse" />
+
+
+
diff --git a/controller/app/src/main/java/org/iiab/controller/env/EnvironmentLock.java b/controller/app/src/main/java/org/iiab/controller/env/EnvironmentLock.java
index 151178b49..a20abdc18 100644
--- a/controller/app/src/main/java/org/iiab/controller/env/EnvironmentLock.java
+++ b/controller/app/src/main/java/org/iiab/controller/env/EnvironmentLock.java
@@ -50,8 +50,9 @@ public final class EnvironmentLock {
public enum Owner { INSTALL, MODULE, BACKUP, RESTORE, CLONE }
/** ADFA-5146: what is actually holding the environment, for a refusal message that names the
- * real cause instead of always saying "an install". */
- public enum Holder { CLONE, BACKUP, RESTORE, INSTALL, DOWNLOAD, NONE }
+ * real cause instead of always saying "an install". ADFA-5333: DASHBOARD = a live dash-node update
+ * is in flight; it restarts the server, so nothing that touches the server may start on top of it. */
+ public enum Holder { CLONE, BACKUP, RESTORE, INSTALL, DOWNLOAD, DASHBOARD, NONE }
// Owner marker: line 1 = Owner.name(), line 2 = epoch millis, line 3 = session token.
private static final String MARKER = ".env_lock";
@@ -180,6 +181,9 @@ public static Holder currentHolder(Context ctx) {
}
}
if (org.iiab.controller.InstallGuard.inProgress(ctx)) return Holder.INSTALL;
+ // ADFA-5333: a live dashboard update restarts dash-node, so it must block every server-touching op
+ // (deep-env ops read this via isHeld; live downloads get an explicit check at their start points).
+ if (org.iiab.controller.redesign.DashboardRebuildService.isRunning()) return Holder.DASHBOARD;
if (isBusyNow()) return Holder.DOWNLOAD; // module installs are caught above by the guard
return Holder.NONE;
}
diff --git a/controller/app/src/main/java/org/iiab/controller/kolibri/presentation/KolibriConfirmFragment.java b/controller/app/src/main/java/org/iiab/controller/kolibri/presentation/KolibriConfirmFragment.java
index 3c31f6e2b..45bf42548 100644
--- a/controller/app/src/main/java/org/iiab/controller/kolibri/presentation/KolibriConfirmFragment.java
+++ b/controller/app/src/main/java/org/iiab/controller/kolibri/presentation/KolibriConfirmFragment.java
@@ -376,6 +376,8 @@ private void bankAndReturn(List chosen) {
* be left with silence, so the refusal is shown.
*/
private void startLive(List chosen) {
+ // ADFA-5333: a live dashboard update restarts dash-node and would break the seed — defer.
+ if (org.iiab.controller.redesign.DashboardRebuild.blockedByUpdate(confirm)) return;
final List toDownload = missingOnly(chosen);
if (toDownload.isEmpty()) {
refuse(R.string.k2go_kolibri_nothing_to_add);
diff --git a/controller/app/src/main/java/org/iiab/controller/kolibri/presentation/KolibriProvisioner.java b/controller/app/src/main/java/org/iiab/controller/kolibri/presentation/KolibriProvisioner.java
index da7a0a3a3..571ddf37f 100644
--- a/controller/app/src/main/java/org/iiab/controller/kolibri/presentation/KolibriProvisioner.java
+++ b/controller/app/src/main/java/org/iiab/controller/kolibri/presentation/KolibriProvisioner.java
@@ -59,8 +59,9 @@ public static boolean hasPending(Context ctx) {
*/
static boolean canDrainNow(Context ctx) {
if (org.iiab.controller.install.presentation.ModuleQueueRepository.get().isRunning()
- || MapsProvisioner.hasPending(ctx)) {
- Log.d(TAG, "kolibri drain blocked: proot (runrole) work is pending/running");
+ || MapsProvisioner.hasPending(ctx)
+ || org.iiab.controller.redesign.DashboardRebuildService.isRunning()) { // ADFA-5333
+ Log.d(TAG, "kolibri drain blocked: proot (runrole) or dashboard-update work is in flight");
return false;
}
// ADFA-5074: unfinished work only. This read a merely registered session, so a
diff --git a/controller/app/src/main/java/org/iiab/controller/redesign/BooksConfirmFragment.java b/controller/app/src/main/java/org/iiab/controller/redesign/BooksConfirmFragment.java
index 997d01738..83ec0909b 100644
--- a/controller/app/src/main/java/org/iiab/controller/redesign/BooksConfirmFragment.java
+++ b/controller/app/src/main/java/org/iiab/controller/redesign/BooksConfirmFragment.java
@@ -79,7 +79,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
if (!(getActivity() instanceof SetupLibraryActivity)) return;
SetupLibraryActivity a = (SetupLibraryActivity) getActivity();
if (banks) a.booksWizardConfirm(); // no box yet: bank it
- else a.startBooksDownload(); // live: download now
+ else if (!DashboardRebuild.blockedByUpdate(v)) a.startBooksDownload(); // live: download now (ADFA-5333)
});
return root;
diff --git a/controller/app/src/main/java/org/iiab/controller/redesign/BooksProvisioner.java b/controller/app/src/main/java/org/iiab/controller/redesign/BooksProvisioner.java
index 7b9590014..74abf91ae 100644
--- a/controller/app/src/main/java/org/iiab/controller/redesign/BooksProvisioner.java
+++ b/controller/app/src/main/java/org/iiab/controller/redesign/BooksProvisioner.java
@@ -41,8 +41,9 @@ public static void drain(Context ctx) {
// background processes and concurrent REST work is a recipe for corruption. Defer REST while a
// module-queue (proot) job is pending or running; a later drain pass picks it up once idle.
if (org.iiab.controller.install.presentation.ModuleQueueRepository.get().isRunning()
- || MapsProvisioner.hasPending(ctx)) {
- Log.d(TAG, "books drain deferred: proot (runrole) work is pending/running");
+ || MapsProvisioner.hasPending(ctx)
+ || DashboardRebuildService.isRunning()) { // ADFA-5333: a live dashboard update restarts dash-node
+ Log.d(TAG, "books drain deferred: proot (runrole) or dashboard-update work is in flight");
return;
}
// ADFA-4954 (ADR-4954 D8): the live REST streams also serialize against each other.
diff --git a/controller/app/src/main/java/org/iiab/controller/redesign/DashboardClient.java b/controller/app/src/main/java/org/iiab/controller/redesign/DashboardClient.java
index a9015e356..a73ab0c82 100644
--- a/controller/app/src/main/java/org/iiab/controller/redesign/DashboardClient.java
+++ b/controller/app/src/main/java/org/iiab/controller/redesign/DashboardClient.java
@@ -34,6 +34,7 @@ private DashboardClient() {}
private static final String URL_UPDATE_CHECK = BoxEndpoints.API + "/system/dashboard/update-check";
private static final String URL_REBUILD = BoxEndpoints.API + "/system/dashboard/rebuild";
private static final String URL_REBUILD_STATUS = BoxEndpoints.API + "/system/dashboard/rebuild/status";
+ private static final String URL_REBUILD_CANCEL = BoxEndpoints.API + "/system/dashboard/rebuild/cancel";
private static final Handler MAIN = new Handler(Looper.getMainLooper());
public interface UpdateCb {
@@ -54,6 +55,14 @@ public interface RebuildStatusCb {
void onErr(String message);
}
+ /** ADFA-5333: result of asking the box to cancel. {@code cancelled} = the rebuild was stopped and the
+ * live dashboard left untouched; {@code promoting} = too late to cancel (the swap is finishing).
+ * Neither true and no error = nothing was running. */
+ public interface RebuildCancelCb {
+ void onResult(boolean cancelled, boolean promoting);
+ void onErr(String message);
+ }
+
/** ADFA-5051: trigger the in-server blue-green rebuild (POST). Fire-and-forget: the box returns 202
* at once (or 409 if one is already running); the caller then polls {@link #rebuildStatus}. */
public static void rebuildStart(RebuildStartCb cb) {
@@ -69,6 +78,39 @@ public static void rebuildStart(RebuildStartCb cb) {
});
}
+ /** ADFA-5333: ask the box to cancel the in-flight rebuild. 200 => cancelled; 409 with promoting=true
+ * => too late (swap finishing); 409 otherwise => nothing running. The body carries the flags, so this
+ * reads it directly rather than going through the shared bodyless POST. */
+ public static void rebuildCancel(RebuildCancelCb cb) {
+ AppExecutors.get().io().execute(() -> {
+ try {
+ HttpURLConnection c = (HttpURLConnection) new URL(URL_REBUILD_CANCEL).openConnection();
+ try {
+ c.setUseCaches(false);
+ c.setConnectTimeout(5000);
+ c.setReadTimeout(10000);
+ c.setRequestMethod("POST");
+ c.setRequestProperty("Accept", "application/json");
+ int code = c.getResponseCode();
+ String body = readAll(code >= 200 && code < 400 ? c.getInputStream() : c.getErrorStream());
+ boolean cancelled = code >= 200 && code < 300;
+ boolean promoting = false;
+ try {
+ JSONObject o = new JSONObject(body);
+ cancelled = o.optBoolean("cancelled", cancelled);
+ promoting = o.optBoolean("promoting", false);
+ } catch (Exception ignore) { /* keep status-derived defaults */ }
+ final boolean fc = cancelled, fp = promoting;
+ MAIN.post(() -> cb.onResult(fc, fp));
+ } finally {
+ c.disconnect();
+ }
+ } catch (Exception e) {
+ MAIN.post(() -> cb.onErr("cancel failed"));
+ }
+ });
+ }
+
/** ADFA-5051: read the rebuild state file the script writes (idle/running/done/error). */
public static void rebuildStatus(RebuildStatusCb cb) {
AppExecutors.get().io().execute(() -> {
diff --git a/controller/app/src/main/java/org/iiab/controller/redesign/DashboardDetailFragment.java b/controller/app/src/main/java/org/iiab/controller/redesign/DashboardDetailFragment.java
index 1a39b0ded..a6d7084bc 100644
--- a/controller/app/src/main/java/org/iiab/controller/redesign/DashboardDetailFragment.java
+++ b/controller/app/src/main/java/org/iiab/controller/redesign/DashboardDetailFragment.java
@@ -12,7 +12,11 @@
*/
package org.iiab.controller.redesign;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -29,6 +33,8 @@
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
+import com.google.android.material.progressindicator.LinearProgressIndicator;
+
import org.iiab.controller.R;
import org.iiab.controller.util.AppExecutors;
@@ -40,6 +46,30 @@ public class DashboardDetailFragment extends Fragment {
private TextView versionChip; // ADFA-5051: "v" chip, updated in place after a live update
private Button rebuild; // de-emphasized when already on the latest
private TextView rebuildHint; // "no rebuild needed" note, shown only when on the latest
+ private View updatingRow; // ADFA-5333: in-progress indicator (indeterminate bar + label + Cancel)
+ private View updatingCancel; // ADFA-5333: the Cancel affordance beside the bar
+ private boolean updating; // ADFA-5333: a background rebuild is in flight; don't re-emphasize Rebuild
+
+ /** ADFA-5333: the live update runs in the background (DashboardRebuildService), which broadcasts each
+ * state change. While this card is on screen we show/hide an in-progress bar and, on done, refresh
+ * the version/pill in place — exactly as the old in-modal completion did. Registered only while
+ * STARTED, so there is no callback captured across the multi-minute detached rebuild. */
+ private final BroadcastReceiver rebuildState = new BroadcastReceiver() {
+ @Override public void onReceive(Context c, Intent i) {
+ if (!isAdded()) return;
+ String state = i.getStringExtra(DashboardRebuildService.EXTRA_STATE);
+ if (DashboardRebuildService.STATE_RUNNING.equals(state)) {
+ setUpdating(true);
+ } else if (DashboardRebuildService.STATE_DONE.equals(state)) {
+ setUpdating(false);
+ refreshAfterLiveUpdate();
+ } else if (DashboardRebuildService.STATE_ERROR.equals(state)
+ || DashboardRebuildService.STATE_CANCELLED.equals(state)) {
+ setUpdating(false);
+ fetchUpdateStatus(); // version unchanged; re-resolve the pill/emphasis
+ }
+ }
+ };
@Nullable
@Override
@@ -73,9 +103,10 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
// "Rebuild"; hide the secondary "Install now".
rebuild = root.findViewById(R.id.k2go_moddet_schedule);
rebuild.setText(R.string.k2go_dash_rebuild);
- rebuild.setOnClickListener(v -> DashboardRebuild.confirmAndStart(this, root, this::refreshAfterLiveUpdate));
+ rebuild.setOnClickListener(v -> DashboardRebuild.confirmAndStart(this, root));
root.findViewById(R.id.k2go_moddet_install_now).setVisibility(View.GONE);
rebuildHint = buildRebuildHint(rebuild); // ADFA-5026: "no rebuild needed" note (hidden until on-latest)
+ updatingRow = buildUpdatingRow(rebuild); // ADFA-5333: in-progress bar (hidden until updating)
// ADFA-5026: resolve the live update status (falls back to the last-known cache when offline).
fetchUpdateStatus();
@@ -83,6 +114,46 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
return root;
}
+ /** ADFA-5333: while visible, listen for the background update's state changes and resolve the current
+ * state on open — so a card entered mid-update (e.g. from the notification) shows the bar. Explicit-
+ * package, not-exported — an internal signal from our own service. */
+ @Override
+ public void onStart() {
+ super.onStart();
+ IntentFilter f = new IntentFilter(DashboardRebuildService.ACTION_STATE);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ requireContext().registerReceiver(rebuildState, f, Context.RECEIVER_NOT_EXPORTED);
+ } else {
+ requireContext().registerReceiver(rebuildState, f);
+ }
+ resolveInitialUpdatingState();
+ }
+
+ @Override
+ public void onStop() {
+ try { requireContext().unregisterReceiver(rebuildState); } catch (IllegalArgumentException ignore) { /* not registered */ }
+ super.onStop();
+ }
+
+ /** Show the bar if an update is in flight. The live service is the fast answer; if it isn't running
+ * but the box itself still reports a rebuild (e.g. our process was killed mid-update), re-own it by
+ * starting the service again — the box replies 409 and the service re-attaches the notification and
+ * the completion broadcast. */
+ private void resolveInitialUpdatingState() {
+ if (DashboardRebuildService.isRunning()) { setUpdating(true); return; }
+ final Context ctx = requireContext().getApplicationContext();
+ DashboardClient.rebuildStatus(new DashboardClient.RebuildStatusCb() {
+ @Override public void onState(String state) {
+ if (!isAdded()) return;
+ if (DashboardRebuildService.STATE_RUNNING.equals(state) && !DashboardRebuildService.isRunning()) {
+ setUpdating(true);
+ DashboardRebuildService.attach(ctx); // re-own without risking a fresh rebuild
+ }
+ }
+ @Override public void onErr(String message) { /* box stopped/offline — nothing in flight to show */ }
+ });
+ }
+
/** ADFA-5026: ask the box whether a newer build exists and reflect it in the status pill + Rebuild
* emphasis. Shows the cached last-known state right away (if any) so the pill isn't stuck on
* "Checking…", then refreshes from the live check; on failure it keeps the cached state. */
@@ -111,6 +182,7 @@ private void applyUpdateStatus(boolean updateAvailable) {
else styleChip(statusChip, getString(R.string.k2go_dash_chip_uptodate), R.color.k2go_leaf);
statusChip.setVisibility(View.VISIBLE);
}
+ if (updating) return; // ADFA-5333: a rebuild is in flight — keep Rebuild disabled and the bar shown
if (rebuild != null) rebuild.setAlpha(updateAvailable ? 1f : 0.6f);
if (rebuildHint != null) rebuildHint.setVisibility(updateAvailable ? View.GONE : View.VISIBLE);
}
@@ -138,6 +210,93 @@ private TextView buildRebuildHint(Button rebuildBtn) {
return hint;
}
+ /** ADFA-5333: an in-progress indicator inserted just above Rebuild — a label, then an M3 indeterminate
+ * bar with a Cancel affordance beside it — shown only while a background update runs (the rebuild
+ * reports no percentage, so the bar is indeterminate). Built in code so the shared module-detail
+ * layout is untouched. */
+ private View buildUpdatingRow(Button rebuildBtn) {
+ ViewGroup parent = (ViewGroup) rebuildBtn.getParent();
+ if (parent == null) return null;
+ float d = getResources().getDisplayMetrics().density;
+ int side = Math.round(20 * d);
+
+ LinearLayout row = new LinearLayout(requireContext());
+ row.setOrientation(LinearLayout.VERTICAL);
+ LinearLayout.LayoutParams rlp = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ rlp.leftMargin = side;
+ rlp.rightMargin = side;
+ rlp.topMargin = Math.round(8 * d);
+ row.setLayoutParams(rlp);
+
+ TextView label = new TextView(requireContext());
+ label.setText(R.string.k2go_dash_live_running);
+ label.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodySmall);
+ label.setTextColor(ContextCompat.getColor(requireContext(), R.color.k2go_muted));
+ row.addView(label);
+
+ // The bar and Cancel sit on one line: bar takes the width, Cancel is right beside it.
+ LinearLayout line = new LinearLayout(requireContext());
+ line.setOrientation(LinearLayout.HORIZONTAL);
+ line.setGravity(android.view.Gravity.CENTER_VERTICAL);
+ LinearLayout.LayoutParams llp = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ llp.topMargin = Math.round(4 * d);
+ line.setLayoutParams(llp);
+
+ LinearProgressIndicator bar = new LinearProgressIndicator(requireContext());
+ bar.setIndeterminate(true);
+ bar.setIndicatorColor(ContextCompat.getColor(requireContext(), R.color.k2go_teal));
+ LinearLayout.LayoutParams blp = new LinearLayout.LayoutParams(
+ 0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f); // weight 1 → takes the remaining width
+ bar.setLayoutParams(blp);
+ line.addView(bar);
+
+ TextView cancel = new TextView(requireContext());
+ cancel.setText(R.string.k2go_dash_cancel);
+ cancel.setAllCaps(true);
+ cancel.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_LabelLarge);
+ cancel.setTextColor(ContextCompat.getColor(requireContext(), R.color.k2go_teal));
+ int hp = Math.round(12 * d), vp = Math.round(6 * d);
+ cancel.setPadding(hp, vp, hp, vp);
+ LinearLayout.LayoutParams clp = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ clp.leftMargin = Math.round(8 * d);
+ cancel.setLayoutParams(clp);
+ android.util.TypedValue tv = new android.util.TypedValue();
+ requireContext().getTheme().resolveAttribute(
+ android.R.attr.selectableItemBackgroundBorderless, tv, true);
+ cancel.setBackgroundResource(tv.resourceId);
+ cancel.setClickable(true);
+ cancel.setOnClickListener(v -> onCancelUpdate());
+ line.addView(cancel);
+ updatingCancel = cancel;
+
+ row.addView(line);
+ row.setVisibility(View.GONE);
+ parent.addView(row, parent.indexOfChild(rebuildBtn));
+ return row;
+ }
+
+ /** Ask the service to cancel; the bar stays until STATE_CANCELLED lands (or the update finishes if it
+ * was too late). Disable the affordance to avoid double taps. */
+ private void onCancelUpdate() {
+ if (updatingCancel != null) updatingCancel.setEnabled(false);
+ Context ctx = requireContext();
+ ContextCompat.startForegroundService(ctx,
+ new android.content.Intent(ctx, DashboardRebuildService.class).setAction(DashboardRebuildService.ACTION_CANCEL));
+ }
+
+ /** Toggle the in-progress state: show/hide the bar and disable Rebuild so it can't be re-triggered
+ * mid-update. On exit, the caller re-resolves the pill/emphasis via {@link #fetchUpdateStatus}. */
+ private void setUpdating(boolean on) {
+ updating = on;
+ if (updatingRow != null) updatingRow.setVisibility(on ? View.VISIBLE : View.GONE);
+ if (updatingCancel != null) updatingCancel.setEnabled(on); // re-enable when a new update shows
+ if (rebuild != null) { rebuild.setEnabled(!on); rebuild.setAlpha(on ? 0.5f : 1f); }
+ if (on && rebuildHint != null) rebuildHint.setVisibility(View.GONE);
+ }
+
/** Read the installed version from the rootfs package.json on disk (authoritative, always present;
* no network/proot) and show a "v" chip. ADFA-5051: reuses the same chip on refresh so a
* live update updates it in place instead of prepending a duplicate. */
diff --git a/controller/app/src/main/java/org/iiab/controller/redesign/DashboardRebuild.java b/controller/app/src/main/java/org/iiab/controller/redesign/DashboardRebuild.java
index e2b36c082..942e1ded2 100644
--- a/controller/app/src/main/java/org/iiab/controller/redesign/DashboardRebuild.java
+++ b/controller/app/src/main/java/org/iiab/controller/redesign/DashboardRebuild.java
@@ -3,15 +3,21 @@
* Name : DashboardRebuild.java
* Author : AppDevForAll
* Copyright : Copyright (c) 2026 AppDevForAll
- * Description : ADFA-5011 / ADFA-5051. Single entry point for starting a dash-node REST-core rebuild,
- * shared by the Module-management hub row and the dashboard detail card. Gated flow:
- * busy check (EnvironmentLock) -> internet check -> confirm dialog -> start.
+ * Description : ADFA-5011 / ADFA-5051 / ADFA-5333. Single entry point for starting a dash-node REST-core
+ * rebuild, shared by the Module-management hub row and the dashboard detail card. Gated
+ * flow: busy check (EnvironmentLock) -> internet check -> confirm dialog -> start.
*
* ADFA-5051 — version-gated cutover: from dash-node 1.2.0 the core updates ITSELF LIVE
* over REST (POST /system/dashboard/rebuild: the blue-green rebuild that stages, smoke-
* tests, atomically swaps and rolls back on failure — no rootfs/proot). Installs still
* on < 1.2.0 predate that, so they take the heavier proot rebuild (InstallService) once
* to reach 1.2.0; from there every later update is the live REST path.
+ *
+ * ADFA-5333 — the LIVE path now runs in the BACKGROUND: it hands off to
+ * DashboardRebuildService (foreground notification) instead of trapping the user behind a
+ * non-cancelable modal. Completion is the server's signal (done/error), not a time cap, so
+ * there is no "taking longer than usual" timeout; a visible card refreshes on the
+ * service's completion broadcast. The proot path (< 1.2.0) is unchanged.
* ============================================================================
*/
package org.iiab.controller.redesign;
@@ -21,18 +27,12 @@
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.view.Gravity;
import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.TextView;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-import com.google.android.material.progressindicator.CircularProgressIndicator;
import org.iiab.controller.R;
import org.iiab.controller.install.presentation.InstallService;
@@ -43,17 +43,11 @@
public final class DashboardRebuild {
private DashboardRebuild() {}
- // Live-rebuild poll cadence + overall cap. The blue-green rebuild does yarn install/build + smoke
- // test + a dash-node restart, so allow a generous ceiling; the restart briefly makes the status
- // endpoint unreachable, which we treat as "keep polling", not a failure.
- private static final long POLL_MS = 2500L;
- private static final int MAX_POLLS = 160; // ~6.5 min
-
- /** Gate then confirm then start. {@code anchor} is where a "busy"/"no internet" snackbar shows.
- * {@code onLiveUpdated} (nullable) runs after a successful LIVE (REST) update so the caller can
- * refresh its version chip / update pill in place (ADFA-5051). */
- public static void confirmAndStart(@NonNull Fragment host, @NonNull View anchor,
- @Nullable Runnable onLiveUpdated) {
+ /** Gate then confirm then start. {@code anchor} is where the "busy"/"no internet"/"started"
+ * snackbars show. A visible dashboard card refreshes itself on
+ * {@link DashboardRebuildService#ACTION_STATE} (ADFA-5333), so there is no completion callback to
+ * pass here — the background update outlives this fragment. */
+ public static void confirmAndStart(@NonNull Fragment host, @NonNull View anchor) {
Context ctx = host.requireContext();
if (org.iiab.controller.env.EnvironmentLock.isHeld(ctx)) {
Snackbars.make(anchor, org.iiab.controller.util.BusyMessage.resFor(ctx)).show();
@@ -67,14 +61,14 @@ public static void confirmAndStart(@NonNull Fragment host, @NonNull View anchor,
.setTitle(R.string.k2go_dash_rebuild_confirm_title)
.setMessage(R.string.k2go_dash_rebuild_confirm_msg)
.setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(R.string.k2go_dash_rebuild, (d, w) -> start(host, anchor, onLiveUpdated))
+ .setPositiveButton(R.string.k2go_dash_rebuild, (d, w) -> start(host, anchor))
.show();
}
/** ADFA-5051: route by the installed dash-node version. >= 1.2.0 updates live over REST; older
* installs take the proot rebuild once as a bridge to 1.2.0. The version read hits disk, so it
* runs off the main thread; the routing itself is posted back to the UI. */
- private static void start(@NonNull Fragment host, @NonNull View anchor, @Nullable Runnable onLiveUpdated) {
+ private static void start(@NonNull Fragment host, @NonNull View anchor) {
final Context app = host.requireContext().getApplicationContext();
final Handler main = new Handler(Looper.getMainLooper());
AppExecutors.get().io().execute(() -> {
@@ -90,7 +84,7 @@ private static void start(@NonNull Fragment host, @NonNull View anchor, @Nullabl
final Operation op = Operation.of("dashboard", Operation.Kind.APP_INSTALL, cls);
main.post(() -> {
if (!host.isAdded()) return;
- if (op.isLive()) startRest(host, anchor, onLiveUpdated);
+ if (op.isLive()) startRest(host, anchor);
else startProot(host);
});
});
@@ -108,86 +102,26 @@ private static void startProot(@NonNull Fragment host) {
.putExtra(SetupProgressActivity.EXTRA_REBUILD, true));
}
- /** ADFA-5051: live REST update. Trigger the in-server rebuild, then show a non-cancelable progress
- * dialog that polls the state until done/error (tolerating the brief restart window). */
- private static void startRest(@NonNull Fragment host, @NonNull View anchor, @Nullable Runnable onLiveUpdated) {
- Context ctx = host.requireContext();
- LinearLayout box = new LinearLayout(ctx);
- box.setOrientation(LinearLayout.HORIZONTAL);
- box.setGravity(Gravity.CENTER_VERTICAL);
- int pad = Math.round(24 * ctx.getResources().getDisplayMetrics().density);
- box.setPadding(pad, pad, pad, pad);
- CircularProgressIndicator spin = new CircularProgressIndicator(ctx);
- spin.setIndeterminate(true);
- box.addView(spin);
- TextView msg = new TextView(ctx);
- msg.setText(R.string.k2go_dash_live_running);
- LinearLayout.LayoutParams mlp = new LinearLayout.LayoutParams(-2, -2);
- mlp.leftMargin = pad;
- msg.setLayoutParams(mlp);
- box.addView(msg);
-
- final AlertDialog dialog = new MaterialAlertDialogBuilder(ctx)
- .setTitle(R.string.k2go_dash_live_title)
- .setView(box)
- .setCancelable(false)
- .show();
-
- final Handler poller = new Handler(Looper.getMainLooper());
- DashboardClient.rebuildStart(new DashboardClient.RebuildStartCb() {
- @Override public void onStarted(boolean alreadyRunning) {
- if (!host.isAdded()) { safeDismiss(dialog); return; }
- pollStatus(host, dialog, poller, new int[]{0}, onLiveUpdated);
- }
- @Override public void onErr(String message) {
- safeDismiss(dialog);
- if (host.isAdded()) Snackbars.make(anchor, R.string.k2go_dash_live_start_failed).show();
- }
- });
- }
-
- /** Poll rebuild state until a terminal one, reusing a single {@code poller} Handler. A status error
- * is transient (dash-node restarts mid-swap) so we keep polling until MAX_POLLS. Hitting the cap is
- * NOT a failure: the rebuild runs detached server-side and may still finish, so we show a distinct
- * "still working in the background" message rather than the rollback/failure one. */
- private static void pollStatus(@NonNull Fragment host, @NonNull AlertDialog dialog,
- @NonNull Handler poller, int[] tries, @Nullable Runnable onLiveUpdated) {
- if (!host.isAdded()) { safeDismiss(dialog); return; }
- if (tries[0]++ >= MAX_POLLS) { finishRest(host, dialog, R.string.k2go_dash_live_timeout, onLiveUpdated); return; }
- DashboardClient.rebuildStatus(new DashboardClient.RebuildStatusCb() {
- @Override public void onState(String state) {
- if (!host.isAdded()) { safeDismiss(dialog); return; }
- if ("done".equals(state)) { finishRest(host, dialog, R.string.k2go_dash_live_done, onLiveUpdated); return; }
- if ("error".equals(state)) { finishRest(host, dialog, R.string.k2go_dash_live_error, onLiveUpdated); return; }
- schedule();
- }
- @Override public void onErr(String message) {
- if (!host.isAdded()) { safeDismiss(dialog); return; }
- schedule(); // API likely restarting mid-swap; keep waiting
- }
- private void schedule() {
- poller.postDelayed(() -> pollStatus(host, dialog, poller, tries, onLiveUpdated), POLL_MS);
- }
- });
+ /** ADFA-5333: live REST update in the background. Hand off to DashboardRebuildService (foreground
+ * notification) and let the user go — the service POSTs the rebuild and polls the box until it
+ * reports done/error, with no time cap. A visible dashboard card refreshes on the service's
+ * completion broadcast; nothing pins this screen. */
+ private static void startRest(@NonNull Fragment host, @NonNull View anchor) {
+ DashboardRebuildService.start(host.requireContext().getApplicationContext());
+ if (host.isAdded()) Snackbars.make(anchor, R.string.k2go_dash_update_started).show();
}
- /** Swap the progress dialog for a simple result dialog carrying {@code msgRes} (done/error/timeout).
- * On success, fire {@code onLiveUpdated} (ADFA-5051) so the caller refreshes its version/pill in
- * place — otherwise the card keeps showing "update available" and users re-tap Rebuild. */
- private static void finishRest(@NonNull Fragment host, @NonNull AlertDialog dialog, int msgRes,
- @Nullable Runnable onLiveUpdated) {
- safeDismiss(dialog);
- if (!host.isAdded()) return;
- if (msgRes == R.string.k2go_dash_live_done && onLiveUpdated != null) onLiveUpdated.run();
- new MaterialAlertDialogBuilder(host.requireContext())
- .setTitle(R.string.k2go_dash_live_title)
- .setMessage(msgRes)
- .setPositiveButton(android.R.string.ok, null)
- .show();
- }
-
- private static void safeDismiss(@NonNull AlertDialog d) {
- try { if (d.isShowing()) d.dismiss(); } catch (Exception ignore) { /* activity gone */ }
+ /** ADFA-5333: reverse gate for LIVE content downloads (ZIM/Books/Kolibri). Those run on the server
+ * and don't consult EnvironmentLock, so they need an explicit check: a dashboard update in flight
+ * restarts dash-node and would break an in-flight download. Returns true (and shows a refusal on
+ * {@code anchor}) when a download must NOT start now. Deep-env ops don't need this — they read the
+ * same fact through EnvironmentLock.isHeld (Holder.DASHBOARD). */
+ public static boolean blockedByUpdate(@NonNull View anchor) {
+ if (DashboardRebuildService.isRunning()) {
+ Snackbars.make(anchor, R.string.k2go_busy_dashboard).show();
+ return true;
+ }
+ return false;
}
/** True when the device reports an internet-capable active network. Unknown -> true (let the
diff --git a/controller/app/src/main/java/org/iiab/controller/redesign/DashboardRebuildService.java b/controller/app/src/main/java/org/iiab/controller/redesign/DashboardRebuildService.java
new file mode 100644
index 000000000..186017f28
--- /dev/null
+++ b/controller/app/src/main/java/org/iiab/controller/redesign/DashboardRebuildService.java
@@ -0,0 +1,246 @@
+/*
+ * ============================================================================
+ * Name : DashboardRebuildService.java
+ * Author : AppDevForAll
+ * Copyright : Copyright (c) 2026 AppDevForAll
+ * Description : ADFA-5333. Runs the LIVE (REST) dash-node update in the background instead of behind a
+ * blocking modal. The dashboard is a REST API with a blue-green self-rebuild: the box does
+ * the work detached (POST /system/dashboard/rebuild returns 202, then stages/smoke-tests/
+ * atomically swaps and rolls back on failure). This foreground service owns only a
+ * notification and the status poll, so the user navigates freely while it runs.
+ *
+ * Completion is the SERVER'S signal, not a clock: it polls the rebuild status until the box
+ * reports a terminal state (done | error) — there is no fixed time cap. A `running` state,
+ * or the brief unreachable window while dash-node restarts mid-swap, just means "keep
+ * waiting". State changes are broadcast ({@link #ACTION_STATE}: running | done | error) so a
+ * visible dashboard card shows an in-progress indicator and refreshes on completion; a
+ * static {@link #isRunning()} lets a card that opens mid-update pick the indicator up.
+ *
+ * The ongoing notification is the way back to that indicator: it is not dismissible while
+ * the update runs, and tapping it deep-links to the dashboard card (Module management ->
+ * Dashboard) rather than cancelling. On a terminal state it becomes a dismissible result.
+ *
+ * Scope: the live REST path only (dash-node >= 1.2.0, routed here by DashboardRebuild). The
+ * proot bridge path (< 1.2.0) genuinely stops the box and keeps its own guarded screen.
+ * ============================================================================
+ */
+package org.iiab.controller.redesign;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.core.app.NotificationCompat;
+import androidx.core.content.ContextCompat;
+
+import org.iiab.controller.R;
+
+public final class DashboardRebuildService extends Service {
+
+ private static final String CHANNEL_ID = "dashboard_update_channel";
+ private static final int NOTIFICATION_ID = 10; // app-global; distinct from the other services
+
+ public static final String ACTION_START = "org.iiab.controller.DASHBOARD_UPDATE_START";
+ /** ADFA-5333: re-own an already-running rebuild (poll only, never POST a new one). */
+ public static final String ACTION_ATTACH = "org.iiab.controller.DASHBOARD_UPDATE_ATTACH";
+ /** ADFA-5333: request a clean cancel (from the notification action or the card button). */
+ public static final String ACTION_CANCEL = "org.iiab.controller.DASHBOARD_UPDATE_CANCEL";
+ /** App-internal broadcast on every state change so a visible card can reflect it live. */
+ public static final String ACTION_STATE = "org.iiab.controller.DASHBOARD_UPDATE_STATE";
+ public static final String EXTRA_STATE = "state";
+ public static final String STATE_RUNNING = "running";
+ public static final String STATE_DONE = "done";
+ public static final String STATE_ERROR = "error";
+ public static final String STATE_CANCELLED = "cancelled";
+
+ private static final long POLL_MS = 2500L;
+
+ /** True while a background rebuild is in flight, so a card opening mid-update shows the indicator
+ * without waiting for a broadcast. Process-scoped; resets to false if the process is recreated. */
+ private static volatile boolean RUNNING = false;
+ public static boolean isRunning() { return RUNNING; }
+
+ private final Handler poller = new Handler(Looper.getMainLooper());
+ private boolean started = false; // one rebuild per service instance; ignore re-delivered starts
+ private boolean cancelling = false; // a cancel request is in flight; ignore repeats
+
+ /** Kick a NEW background live update (POST + poll). */
+ public static void start(Context ctx) {
+ ContextCompat.startForegroundService(ctx,
+ new Intent(ctx, DashboardRebuildService.class).setAction(ACTION_START));
+ }
+
+ /** Re-own a rebuild that is ALREADY running on the box (e.g. our process was killed mid-update): poll
+ * it to completion and restore the notification, but never POST a new rebuild — so a card that sees
+ * the box still building can attach without risking a second run if the original just finished. */
+ public static void attach(Context ctx) {
+ ContextCompat.startForegroundService(ctx,
+ new Intent(ctx, DashboardRebuildService.class).setAction(ACTION_ATTACH));
+ }
+
+ @Override public void onCreate() { super.onCreate(); createNotificationChannel(); }
+ @Nullable @Override public IBinder onBind(Intent intent) { return null; }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ // Every startForegroundService() must be answered by startForeground(), including a redundant
+ // start delivered while a rebuild is already in flight (user re-taps Rebuild) or a cancel request
+ // — otherwise the system raises "did not call startForeground". It's idempotent, so satisfy the
+ // contract first.
+ startForeground(NOTIFICATION_ID, buildOngoing());
+ final String action = intent != null ? intent.getAction() : null;
+ if (ACTION_CANCEL.equals(action)) { requestCancel(); return START_NOT_STICKY; }
+ if (started) return START_NOT_STICKY;
+ started = true;
+ RUNNING = true;
+ broadcastState(STATE_RUNNING);
+ if (ACTION_ATTACH.equals(action)) {
+ // Re-own: the box is already building, so just poll to completion (a first poll that finds
+ // done/error finishes at once — no new rebuild is ever launched).
+ pollStatus();
+ } else {
+ DashboardClient.rebuildStart(new DashboardClient.RebuildStartCb() {
+ @Override public void onStarted(boolean alreadyRunning) { pollStatus(); }
+ @Override public void onErr(String message) { finish(STATE_ERROR, R.string.k2go_dash_live_start_failed); }
+ });
+ }
+ return START_NOT_STICKY;
+ }
+
+ /** ADFA-5333: ask the box to cancel. On success stop cleanly (the box left the live dashboard as it
+ * was); if it's too late (promoting) or unreachable, keep running and let the update finish — the
+ * swap is seconds away, so a half-cancel is worse than letting it complete. */
+ private void requestCancel() {
+ if (cancelling) return;
+ cancelling = true;
+ DashboardClient.rebuildCancel(new DashboardClient.RebuildCancelCb() {
+ @Override public void onResult(boolean cancelled, boolean promoting) {
+ if (cancelled) { finishCancelled(); return; }
+ cancelling = false;
+ // Not cancelled. If this instance isn't actually running a rebuild (a stale cancel-only
+ // start on an old notification), clear the device side so no orphan notification lingers.
+ if (!started) { finishCancelled(); return; }
+ // Still updating: re-assert RUNNING so a visible card re-enables its Cancel, then say why.
+ broadcastState(STATE_RUNNING);
+ toast(promoting ? R.string.k2go_dash_cancel_too_late : R.string.k2go_dash_cancel_failed);
+ }
+ @Override public void onErr(String message) {
+ cancelling = false;
+ if (!started) { finishCancelled(); return; } // couldn't reach the box, nothing to keep
+ broadcastState(STATE_RUNNING); // update keeps going; let the card retry
+ toast(R.string.k2go_dash_cancel_failed);
+ }
+ });
+ }
+
+ /** Cancelled cleanly: drop the poll and the notification entirely (leave nothing behind) and tell a
+ * visible card to re-enable Rebuild. */
+ private void finishCancelled() {
+ poller.removeCallbacksAndMessages(null);
+ RUNNING = false;
+ stopForeground(Service.STOP_FOREGROUND_REMOVE);
+ broadcastState(STATE_CANCELLED);
+ stopSelf();
+ }
+
+ private void toast(@StringRes int msgRes) {
+ android.widget.Toast.makeText(getApplicationContext(), msgRes, android.widget.Toast.LENGTH_LONG).show();
+ }
+
+ /** Poll the box until it reports a terminal state. No time cap: completion is the server's signal
+ * (done/error), not a clock. A `running` state or a transient unreachable window (dash-node
+ * restarting during the swap) just means "keep waiting". */
+ private void pollStatus() {
+ DashboardClient.rebuildStatus(new DashboardClient.RebuildStatusCb() {
+ @Override public void onState(String state) {
+ if (STATE_DONE.equals(state)) { finish(STATE_DONE, R.string.k2go_dash_live_done); return; }
+ if (STATE_ERROR.equals(state)) { finish(STATE_ERROR, R.string.k2go_dash_live_error); return; }
+ schedule();
+ }
+ @Override public void onErr(String message) { schedule(); } // restarting mid-swap; keep waiting
+ });
+ }
+
+ private void schedule() { poller.postDelayed(this::pollStatus, POLL_MS); }
+
+ /** Terminal: replace the ongoing notification with a final dismissible one, broadcast the result so a
+ * visible card hides the indicator and refreshes in place, then stop. */
+ private void finish(String state, @StringRes int msgRes) {
+ poller.removeCallbacksAndMessages(null);
+ RUNNING = false;
+ // Keep the final notification on screen after we drop the foreground state.
+ stopForeground(Service.STOP_FOREGROUND_DETACH);
+ NotificationManager m = getSystemService(NotificationManager.class);
+ if (m != null) m.notify(NOTIFICATION_ID, buildFinal(msgRes));
+ broadcastState(state);
+ stopSelf();
+ }
+
+ @Override public void onDestroy() { RUNNING = false; super.onDestroy(); } // safety if torn down early
+
+ private void broadcastState(String state) {
+ sendBroadcast(new Intent(ACTION_STATE).setPackage(getPackageName()).putExtra(EXTRA_STATE, state));
+ }
+
+ private void createNotificationChannel() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel ch = new NotificationChannel(
+ CHANNEL_ID, getString(R.string.k2go_dash_update_channel_name), NotificationManager.IMPORTANCE_LOW);
+ NotificationManager m = getSystemService(NotificationManager.class);
+ if (m != null) m.createNotificationChannel(ch);
+ }
+ }
+
+ /** Deep-link to Module management -> Dashboard (the card that shows the in-progress indicator), so the
+ * notification is a way back into the update rather than a dead end. */
+ private PendingIntent openDashboardDetail() {
+ Intent openI = new Intent(this, SetupLibraryActivity.class)
+ .putExtra(SetupLibraryActivity.EXTRA_MODULE_MGMT, true)
+ .putExtra(SetupLibraryActivity.EXTRA_DASHBOARD_DETAIL, true)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ return PendingIntent.getActivity(this, 0, openI,
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ /** Ongoing "updating…" notification. Not dismissible and does NOT auto-cancel on tap — while the
+ * update runs it stays put as the way back to the in-app indicator. Carries a Cancel action. */
+ private Notification buildOngoing() {
+ PendingIntent cancel = PendingIntent.getService(this, 1,
+ new Intent(this, DashboardRebuildService.class).setAction(ACTION_CANCEL),
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
+ return new NotificationCompat.Builder(this, CHANNEL_ID)
+ .setContentTitle(getString(R.string.k2go_dash_live_title))
+ .setContentText(getString(R.string.k2go_dash_live_running))
+ .setSmallIcon(android.R.drawable.stat_sys_download)
+ .setContentIntent(openDashboardDetail())
+ .setOngoing(true)
+ .setAutoCancel(false)
+ .setPriority(NotificationCompat.PRIORITY_LOW)
+ .setOnlyAlertOnce(true)
+ .addAction(0, getString(R.string.k2go_dash_cancel), cancel)
+ .build();
+ }
+
+ /** Final result notification (done/error) — dismissible, auto-cancels on tap. */
+ private Notification buildFinal(@StringRes int msgRes) {
+ return new NotificationCompat.Builder(this, CHANNEL_ID)
+ .setContentTitle(getString(R.string.k2go_dash_live_title))
+ .setContentText(getString(msgRes))
+ .setSmallIcon(android.R.drawable.stat_sys_download_done)
+ .setContentIntent(openDashboardDetail())
+ .setAutoCancel(true)
+ .setOngoing(false)
+ .setPriority(NotificationCompat.PRIORITY_LOW)
+ .build();
+ }
+}
diff --git a/controller/app/src/main/java/org/iiab/controller/redesign/ModuleHubFragment.java b/controller/app/src/main/java/org/iiab/controller/redesign/ModuleHubFragment.java
index 573796f2d..cc0865142 100644
--- a/controller/app/src/main/java/org/iiab/controller/redesign/ModuleHubFragment.java
+++ b/controller/app/src/main/java/org/iiab/controller/redesign/ModuleHubFragment.java
@@ -546,7 +546,7 @@ private void addSystemDashboardCard() {
TextView rebuild = statePill(getString(R.string.k2go_dash_rebuild), R.color.k2go_teal);
rebuild.setPadding(px(14), px(6), px(14), px(6));
- rebuild.setOnClickListener(v -> DashboardRebuild.confirmAndStart(this, host, null));
+ rebuild.setOnClickListener(v -> DashboardRebuild.confirmAndStart(this, host));
LinearLayout.LayoutParams tlp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
tlp.leftMargin = px(10);
diff --git a/controller/app/src/main/java/org/iiab/controller/redesign/SetupLibraryActivity.java b/controller/app/src/main/java/org/iiab/controller/redesign/SetupLibraryActivity.java
index d81a7166f..245c1c310 100644
--- a/controller/app/src/main/java/org/iiab/controller/redesign/SetupLibraryActivity.java
+++ b/controller/app/src/main/java/org/iiab/controller/redesign/SetupLibraryActivity.java
@@ -34,6 +34,9 @@ public class SetupLibraryActivity extends AppCompatActivity implements org.iiab.
public static final String EXTRA_MODULE_MGMT = "moduleMgmt";
/** ADFA-4958: deep-link to a specific module's detail from Home (opens the hub, then the detail). */
public static final String EXTRA_MODULE_DETAIL = "moduleDetail";
+ /** ADFA-5333: deep-link straight to the dashboard detail (opens the hub, then the dashboard card) —
+ * used by the background-update notification so tapping it lands on the in-progress indicator. */
+ public static final String EXTRA_DASHBOARD_DETAIL = "dashboardDetail";
/** ADFA-4958: open the maps content selector directly from Home (maps is a module with a selector step). */
public static final String EXTRA_MAPS_SETUP = "mapsSetup";
/** ADFA-5004: open the Wikipedia & ZIM content screen directly (from the reader's Get-more shortcut). */
@@ -127,6 +130,7 @@ protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState == null) {
boolean moduleMgmt = getIntent().getBooleanExtra(EXTRA_MODULE_MGMT, false);
final String moduleDetail = getIntent().getStringExtra(EXTRA_MODULE_DETAIL);
+ final boolean dashboardDetail = getIntent().getBooleanExtra(EXTRA_DASHBOARD_DETAIL, false); // ADFA-5333
boolean mapsSetup = getIntent().getBooleanExtra(EXTRA_MAPS_SETUP, false);
boolean zimSetup = getIntent().getBooleanExtra(EXTRA_ZIM_SETUP, false);
boolean backupRestore = getIntent().getBooleanExtra(EXTRA_BACKUP_RESTORE, false);
@@ -137,7 +141,7 @@ protected void onCreate(Bundle savedInstanceState) {
first = BackupJobFragment.newInstance(brJobMode); // ADFA-4957: land on the live op screen
} else if (backupRestore) {
first = new BackupRestoreFragment(); // ADFA-4952
- } else if (moduleMgmt || moduleDetail != null) {
+ } else if (moduleMgmt || moduleDetail != null || dashboardDetail) {
selectedTier = readInstalledTier(); // ADFA-4842: module management hub (proot apps)
first = new ModuleHubFragment();
} else if (mapsSetup) {
@@ -169,6 +173,23 @@ protected void onCreate(Bundle savedInstanceState) {
.commit();
if (moduleDetail != null) {
findViewById(R.id.k2go_setup_host).post(() -> openModuleDetail(moduleDetail));
+ } else if (dashboardDetail) { // ADFA-5333: land on the dashboard card (its in-progress indicator)
+ findViewById(R.id.k2go_setup_host).post(this::openDashboardDetail);
+ }
+ }
+ }
+
+ /** ADFA-5333: the background-update notification uses SINGLE_TOP|CLEAR_TOP, so when this activity is
+ * already alive the tap arrives here rather than through onCreate. Route to the dashboard detail
+ * unless it is already on top (don't stack duplicates). */
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ setIntent(intent);
+ if (intent != null && intent.getBooleanExtra(EXTRA_DASHBOARD_DETAIL, false)) {
+ Fragment top = getSupportFragmentManager().findFragmentById(R.id.k2go_setup_host);
+ if (!(top instanceof DashboardDetailFragment)) {
+ findViewById(R.id.k2go_setup_host).post(this::openDashboardDetail);
}
}
}
diff --git a/controller/app/src/main/java/org/iiab/controller/redesign/ZimConfirmFragment.java b/controller/app/src/main/java/org/iiab/controller/redesign/ZimConfirmFragment.java
index 4d79f9ead..67ccad123 100644
--- a/controller/app/src/main/java/org/iiab/controller/redesign/ZimConfirmFragment.java
+++ b/controller/app/src/main/java/org/iiab/controller/redesign/ZimConfirmFragment.java
@@ -106,7 +106,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
if (!(getActivity() instanceof SetupLibraryActivity)) return;
SetupLibraryActivity a = (SetupLibraryActivity) getActivity();
if (banks) a.zimWizardConfirm(); // no box yet: bank it
- else a.startZimDownload(); // ADFA-5074: start it, then land on the index
+ else if (!DashboardRebuild.blockedByUpdate(v)) a.startZimDownload(); // ADFA-5074 / ADFA-5333
});
return root;
diff --git a/controller/app/src/main/java/org/iiab/controller/redesign/ZimProvisioner.java b/controller/app/src/main/java/org/iiab/controller/redesign/ZimProvisioner.java
index 2b6e32c54..4fcc48bd3 100644
--- a/controller/app/src/main/java/org/iiab/controller/redesign/ZimProvisioner.java
+++ b/controller/app/src/main/java/org/iiab/controller/redesign/ZimProvisioner.java
@@ -42,8 +42,9 @@ public static void drain(Context ctx) {
// background processes; concurrent REST work risks corruption). Defer ZIM while a module-queue
// (proot) job is pending or running; a later drain pass runs it once proot is idle.
if (org.iiab.controller.install.presentation.ModuleQueueRepository.get().isRunning()
- || MapsProvisioner.hasPending(ctx)) {
- Log.d(TAG, "zim drain deferred: proot (runrole) work is pending/running");
+ || MapsProvisioner.hasPending(ctx)
+ || DashboardRebuildService.isRunning()) { // ADFA-5333: a live dashboard update restarts dash-node
+ Log.d(TAG, "zim drain deferred: proot (runrole) or dashboard-update work is in flight");
return;
}
// ADFA-4954 (ADR-4954 D8): the live REST streams also serialize against each other.
diff --git a/controller/app/src/main/java/org/iiab/controller/util/BusyMessage.java b/controller/app/src/main/java/org/iiab/controller/util/BusyMessage.java
index 18cf147aa..07751ced3 100644
--- a/controller/app/src/main/java/org/iiab/controller/util/BusyMessage.java
+++ b/controller/app/src/main/java/org/iiab/controller/util/BusyMessage.java
@@ -29,6 +29,7 @@ public static int resFor(Context ctx) {
case BACKUP: return R.string.k2go_busy_backup;
case RESTORE: return R.string.k2go_busy_restore;
case DOWNLOAD: return R.string.k2go_busy_download;
+ case DASHBOARD: return R.string.k2go_busy_dashboard; // ADFA-5333
case INSTALL:
default: return R.string.k2go_busy_install; // NONE is not expected at a refusal
}
diff --git a/controller/app/src/main/res/values-ar/strings.xml b/controller/app/src/main/res/values-ar/strings.xml
index 23eee32c1..e9ab4aca9 100644
--- a/controller/app/src/main/res/values-ar/strings.xml
+++ b/controller/app/src/main/res/values-ar/strings.xml
@@ -1115,14 +1115,18 @@
نواة REST · مثبَّت v%1$s
إعادة البناء
إعادة بناء نواة REST؟
- تُحدِّث dash-node إلى أحدث إصدار. يكون الخادم غير متاح لفترة وجيزة أثناء إعادة البناء؛ لا تغادر هذه الشاشة حتى تنتهي.
+ يُحدّث dash-node إلى أحدث إصدار. يعمل في الخلفية؛ يكون الخادم غير متاح لفترة وجيزة أثناء إعادة البناء.
تتطلب إعادة البناء اتصالاً بالإنترنت.
جارٍ تحديث لوحة التحكم
جارٍ التحديث في مكانه… ستُعاد تشغيل لوحة التحكم لفترة وجيزة.
تم تحديث لوحة التحكم.
فشل التحديث — تم الاحتفاظ بالإصدار السابق.
- يستغرق التحديث وقتًا أطول من المعتاد. سيكتمل في الخلفية — تحقّق من الإصدار بعد قليل.
تعذّر بدء التحديث. حاول مرة أخرى.
+ جارٍ تحديث لوحة المعلومات في الخلفية.
+ تحديث لوحة المعلومات
+ إلغاء
+ التحديث يوشك على الانتهاء ولا يمكن إلغاؤه الآن.
+ تعذّر إلغاء التحديث. سيستمر في العمل.
Dashboard (REST API)
نواة النظام · مثبَّتة دائمًا
لوحة التحكم هي نواة واجهة REST API للجهاز (dash-node) — خدمة Node صغيرة على 127.0.0.1:4000، تُقدَّم عبر nginx ويشرف عليها pdsm. تشغّل كل إجراء للمكتبة داخل التطبيق: تصفّح وتنزيل الكتب، إضافة وحذف محتوى Wikipedia وZIM، جلسات Kolibri، الخرائط وحالة النظام. على عكس وحدات المحتوى، لا يمكنك تثبيتها أو إزالتها. تأتي مع التطبيق وموجودة دائمًا. عند توفّر إصدار أحدث، يمكنك إعادة بنائها في مكانها.
@@ -1238,6 +1242,7 @@
توجد استعادة قيد التنفيذ. يرجى الانتظار حتى تنتهي.
يوجد تثبيت قيد التنفيذ. يرجى الانتظار حتى ينتهي.
يوجد تنزيل قيد التنفيذ. يرجى الانتظار حتى ينتهي.
+ يجري تحديث لوحة المعلومات. يُرجى الانتظار حتى ينتهي.
التنزيلات المعلقة
لا توجد تنزيلات معلقة.
يجري تنزيل شيء الآن
diff --git a/controller/app/src/main/res/values-az/strings.xml b/controller/app/src/main/res/values-az/strings.xml
index 2fa246848..623807465 100644
--- a/controller/app/src/main/res/values-az/strings.xml
+++ b/controller/app/src/main/res/values-az/strings.xml
@@ -1135,14 +1135,18 @@
REST nüvəsi · quraşdırılıb v%1$s
Yenidən qur
REST nüvəsi yenidən qurulsun?
- dash-node-u ən son versiyaya yeniləyir. Yenidən qurma zamanı server qısa müddət əlçatmaz olur; bitənə qədər bu ekranı tərk etməyin.
+ dash-node-u ən son versiyaya yeniləyir. Arxa planda işləyir; yenidən qurulan zaman server qısa müddət əlçatmaz olur.
Yenidən qurma internet bağlantısı tələb edir.
İdarə paneli yenilənir
Yerində yenilənir… idarə paneli qısa müddət yenidən başladılacaq.
İdarə paneli yeniləndi.
Yeniləmə alınmadı — əvvəlki versiya saxlanıldı.
- Yeniləmə adətən olduğundan uzun çəkir. Arxa planda tamamlanacaq — bir azdan versiyanı yoxlayın.
Yeniləməni başlatmaq alınmadı. Yenidən cəhd edin.
+ İdarə paneli arxa planda yenilənir.
+ İdarə paneli yeniləməsi
+ Ləğv et
+ Yeniləmə bitmək üzrədir və indi ləğv edilə bilməz.
+ Yeniləməni ləğv etmək alınmadı. İşləməyə davam edəcək.
Dashboard (REST API)
Sistem nüvəsi · həmişə quraşdırılıb
Panel cihazın REST API nüvəsidir (dash-node) — 127.0.0.1:4000 ünvanında kiçik Node xidmətidir, nginx ilə təqdim olunur və pdsm ilə nəzarət olunur. Tətbiqdəki hər kitabxana əməliyyatını idarə edir: Kitablara baxış və endirmə, Wikipedia və ZIM məzmununun əlavəsi və silinməsi, Kolibri sessiyaları, Xəritələr və sistem statusu. Məzmun modullarından fərqli olaraq, onu quraşdıra və ya silə bilməzsən. Tətbiqlə birlikdə gəlir və həmişə mövcuddur. Daha yeni versiya mövcud olduqda onu yerində yenidən qura bilərsən.
@@ -1258,6 +1262,7 @@
Bərpa gedir. Bitməsini gözlə.
Quraşdırma gedir. Bitməsini gözlə.
Endirmə gedir. Bitməsini gözlə.
+ İdarə paneli yenilənir. Bitməsini gözləyin.
Gözləyən yükləmələr
Gözləyən yükləmə yoxdur.
İndi nəsə yüklənir
diff --git a/controller/app/src/main/res/values-bg/strings.xml b/controller/app/src/main/res/values-bg/strings.xml
index cadf2159c..3cd970df2 100644
--- a/controller/app/src/main/res/values-bg/strings.xml
+++ b/controller/app/src/main/res/values-bg/strings.xml
@@ -1122,14 +1122,18 @@
REST ядро · инсталирана v%1$s
Пресъздаване
Да се пресъздаде ли REST ядрото?
- Актуализира dash-node до най-новата версия. Сървърът е за кратко недостъпен по време на пресъздаването; не напускайте този екран, докато не приключи.
+ Актуализира dash-node до най-новата версия. Изпълнява се на заден план; сървърът е кратко недостъпен, докато се пресъздава.
Пресъздаването изисква интернет връзка.
Обновяване на таблото
Обновяване на място… таблото се рестартира за кратко.
Таблото е обновено.
Обновяването е неуспешно — предишната версия е запазена.
- Актуализацията отнема повече от обичайното. Ще завърши на заден план — проверете версията след малко.
Обновяването не можа да започне. Опитайте отново.
+ Таблото се актуализира на заден план.
+ Актуализация на таблото
+ Отказ
+ Актуализацията приключва и не може да бъде отменена сега.
+ Актуализацията не можа да бъде отменена. Ще продължи да работи.
Dashboard (REST API)
Системно ядро · винаги инсталирано
Таблото е ядрото на REST API на устройството (dash-node) — малка Node услуга на 127.0.0.1:4000, обслужвана от nginx и наблюдавана от pdsm. То задвижва всяко действие на библиотеката в приложението: разглеждане и изтегляне на Книги, добавяне и изтриване на съдържание от Wikipedia и ZIM, сесии на Kolibri, Карти и състояние на системата. За разлика от модулите със съдържание, не можете да го инсталирате или премахнете. Идва с приложението и винаги присъства. Когато е налична по-нова версия, можете да го изградите наново на място.
@@ -1245,6 +1249,7 @@
Извършва се възстановяване. Изчакайте да приключи.
Извършва се инсталиране. Изчакайте да приключи.
Извършва се изтегляне. Изчакайте да приключи.
+ Извършва се актуализация на таблото. Моля, изчакайте да завърши.
Чакащи изтегляния
Няма чакащи изтегляния.
В момента се изтегля нещо
diff --git a/controller/app/src/main/res/values-bn/strings.xml b/controller/app/src/main/res/values-bn/strings.xml
index 7e0c31ba3..70aa6314b 100644
--- a/controller/app/src/main/res/values-bn/strings.xml
+++ b/controller/app/src/main/res/values-bn/strings.xml
@@ -1128,14 +1128,18 @@
REST কোর · ইনস্টল করা v%1$s
পুনর্নির্মাণ
REST কোর পুনর্নির্মাণ করবেন?
- dash-node কে সর্বশেষ সংস্করণে আপডেট করে। পুনর্নির্মাণের সময় সার্ভার কিছুক্ষণের জন্য অনুপলব্ধ থাকে; শেষ না হওয়া পর্যন্ত এই স্ক্রিন ছেড়ে যাবেন না।
+ dash-node কে সর্বশেষ সংস্করণে আপডেট করে। এটি ব্যাকগ্রাউন্ডে চলে; পুনর্নির্মাণের সময় সার্ভার কিছুক্ষণ অনুপলব্ধ থাকে।
পুনর্নির্মাণের জন্য তারপর আপনি উপায় বেছে নেবেন: ডাউনলোড করা বা অন্য ফোন থেকে কপি করা।
ড্যাশবোর্ড আপডেট হচ্ছে
স্থানেই আপডেট হচ্ছে… ড্যাশবোর্ড কিছুক্ষণের জন্য রিস্টার্ট হবে।
ড্যাশবোর্ড আপডেট হয়েছে।
আপডেট ব্যর্থ হয়েছে — পূর্ববর্তী সংস্করণটি রাখা হয়েছে।
- আপডেটটি স্বাভাবিকের চেয়ে বেশি সময় নিচ্ছে। এটি ব্যাকগ্রাউন্ডে সম্পন্ন হবে — একটু পরে সংস্করণটি দেখুন।
আপডেট শুরু করা যায়নি। আবার চেষ্টা করুন।
+ ড্যাশবোর্ড ব্যাকগ্রাউন্ডে আপডেট হচ্ছে।
+ ড্যাশবোর্ড আপডেট
+ বাতিল
+ আপডেটটি শেষ হচ্ছে এবং এখন বাতিল করা যাবে না।
+ আপডেট বাতিল করা যায়নি। এটি চলতে থাকবে।
Dashboard (REST API)
সিস্টেম কোর · সর্বদা ইনস্টল করা
ড্যাশবোর্ড হলো বক্সের REST API কোর (dash-node) — 127.0.0.1:4000-এ একটি ছোট Node পরিষেবা, যা nginx দিয়ে পরিবেশিত ও pdsm দিয়ে তত্ত্বাবধান করা হয়। এটি অ্যাপের প্রতিটি লাইব্রেরি কাজ চালায়: বই ব্রাউজ ও ডাউনলোড, Wikipedia ও ZIM কনটেন্ট যোগ ও মুছে ফেলা, Kolibri সেশন, মানচিত্র ও সিস্টেম স্ট্যাটাস। কনটেন্ট মডিউলের বিপরীতে, আপনি এটি ইনস্টল বা সরাতে পারবেন না। এটি অ্যাপের সাথে আসে এবং সবসময় থাকে। নতুন বিল্ড উপলব্ধ হলে, আপনি এটি জায়গাতেই আবার তৈরি করতে পারেন।
@@ -1251,6 +1255,7 @@
একটি পুনরুদ্ধার চলছে। অনুগ্রহ করে শেষ হওয়া পর্যন্ত অপেক্ষা করুন।
একটি ইনস্টল চলছে। অনুগ্রহ করে শেষ হওয়া পর্যন্ত অপেক্ষা করুন।
একটি ডাউনলোড চলছে। অনুগ্রহ করে শেষ হওয়া পর্যন্ত অপেক্ষা করুন।
+ ড্যাশবোর্ড আপডেট চলছে। শেষ হওয়া পর্যন্ত অপেক্ষা করুন।
মুলতুবি ডাউনলোড
কোনো মুলতুবি ডাউনলোড নেই।
এখন কিছু ডাউনলোড হচ্ছে
diff --git a/controller/app/src/main/res/values-cs/strings.xml b/controller/app/src/main/res/values-cs/strings.xml
index f4ab07730..466193221 100644
--- a/controller/app/src/main/res/values-cs/strings.xml
+++ b/controller/app/src/main/res/values-cs/strings.xml
@@ -1122,14 +1122,18 @@
Jádro REST · nainstalováno v%1$s
Přestavět
Přestavět jádro REST?
- Aktualizuje dash-node na nejnovější verzi. Server je během přestavby krátce nedostupný; neopouštějte tuto obrazovku, dokud neskončí.
+ Aktualizuje dash-node na nejnovější verzi. Běží na pozadí; server je během obnovování krátce nedostupný.
Přestavba vyžaduje připojení k internetu.
Aktualizace nástěnky
Aktualizace na místě… nástěnka se na chvíli restartuje.
Nástěnka je aktualizována.
Aktualizace selhala — předchozí verze byla zachována.
- Aktualizace trvá déle než obvykle. Dokončí se na pozadí — za chvíli zkontrolujte verzi.
Aktualizaci se nepodařilo spustit. Zkuste to znovu.
+ Panel se aktualizuje na pozadí.
+ Aktualizace panelu
+ Zrušit
+ Aktualizace se dokončuje a nyní ji nelze zrušit.
+ Aktualizaci se nepodařilo zrušit. Bude pokračovat.
Dashboard (REST API)
Jádro systému · vždy nainstalováno
Ovládací panel je jádro REST API zařízení (dash-node) — malá služba Node na 127.0.0.1:4000, obsluhovaná přes nginx a spravovaná pomocí pdsm. Pohání každou akci knihovny v aplikaci: procházení a stahování Knih, přidávání a mazání obsahu Wikipedie a ZIM, relace Kolibri, Mapy a stav systému. Na rozdíl od modulů obsahu ho nemůžeš nainstalovat ani odebrat. Je součástí aplikace a je vždy přítomný. Když je k dispozici novější sestavení, můžeš ho na místě znovu sestavit.
@@ -1245,6 +1249,7 @@
Probíhá obnovení. Počkej, až skončí.
Probíhá instalace. Počkej, až skončí.
Probíhá stahování. Počkej, až skončí.
+ Probíhá aktualizace panelu. Počkejte prosím na dokončení.
Čekající stahování
Žádná čekající stahování.
Něco se právě stahuje
diff --git a/controller/app/src/main/res/values-de/strings.xml b/controller/app/src/main/res/values-de/strings.xml
index 30ef6a600..b6d74167c 100644
--- a/controller/app/src/main/res/values-de/strings.xml
+++ b/controller/app/src/main/res/values-de/strings.xml
@@ -1115,14 +1115,18 @@
REST-Kern · installiert v%1$s
Neu erstellen
REST-Kern neu erstellen?
- Aktualisiert dash-node auf die neueste Version. Der Server ist während der Neuerstellung kurz nicht verfügbar; verlassen Sie diesen Bildschirm nicht, bis es fertig ist.
+ Aktualisiert dash-node auf die neueste Version. Es läuft im Hintergrund; der Server ist während der Neuerstellung kurz nicht verfügbar.
Für die Neuerstellung ist eine Internetverbindung nötig.
Dashboard wird aktualisiert
Aktualisierung an Ort und Stelle… das Dashboard startet kurz neu.
Dashboard aktualisiert.
Aktualisierung fehlgeschlagen — die vorherige Version wurde beibehalten.
- Die Aktualisierung dauert länger als üblich. Es wird im Hintergrund abgeschlossen — prüfe die Version gleich.
Aktualisierung konnte nicht gestartet werden. Erneut versuchen.
+ Dashboard wird im Hintergrund aktualisiert.
+ Dashboard-Aktualisierung
+ Abbrechen
+ Das Update wird abgeschlossen und kann jetzt nicht abgebrochen werden.
+ Update konnte nicht abgebrochen werden. Es läuft weiter.
Dashboard (REST API)
Systemkern · immer installiert
Das Dashboard ist der REST-API-Kern der Box (dash-node) – ein kleiner Node-Dienst auf 127.0.0.1:4000, über nginx bereitgestellt und von pdsm überwacht. Es steuert jede Bibliotheksaktion in der App: Bücher durchsuchen und herunterladen, Wikipedia- und ZIM-Inhalte hinzufügen und löschen, Kolibri-Sitzungen, Karten und Systemstatus. Anders als die Inhaltsmodule kannst du es nicht installieren oder entfernen. Es wird mit der App geliefert und ist immer vorhanden. Wenn eine neuere Version verfügbar ist, kannst du es an Ort und Stelle neu erstellen.
@@ -1238,6 +1242,7 @@
Eine Wiederherstellung läuft. Bitte warte, bis sie abgeschlossen ist.
Eine Installation läuft. Bitte warte, bis sie abgeschlossen ist.
Ein Download läuft. Bitte warte, bis er abgeschlossen ist.
+ Ein Dashboard-Update läuft. Bitte warte, bis es abgeschlossen ist.
Ausstehende Downloads
Keine ausstehenden Downloads.
Es wird gerade etwas heruntergeladen
diff --git a/controller/app/src/main/res/values-el/strings.xml b/controller/app/src/main/res/values-el/strings.xml
index 3cb1081e5..cc2c19c0c 100644
--- a/controller/app/src/main/res/values-el/strings.xml
+++ b/controller/app/src/main/res/values-el/strings.xml
@@ -1122,14 +1122,18 @@
Πυρήνας REST · εγκατεστημένη v%1$s
Ανασυγκρότηση
Ανασυγκρότηση του πυρήνα REST;
- Ενημερώνει το dash-node στην πιο πρόσφατη έκδοση. Ο διακομιστής είναι για λίγο μη διαθέσιμος κατά την ανασυγκρότηση· μην φύγετε από αυτήν την οθόνη μέχρι να ολοκληρωθεί.
+ Ενημερώνει το dash-node στην πιο πρόσφατη έκδοση. Εκτελείται στο παρασκήνιο· ο διακομιστής είναι για λίγο μη διαθέσιμος κατά την αναδόμηση.
Η ανασυγκρότηση χρειάζεται σύνδεση στο διαδίκτυο.
Ενημέρωση πίνακα
Ενημέρωση επιτόπου… ο πίνακας επανεκκινείται για λίγο.
Ο πίνακας ενημερώθηκε.
Η ενημέρωση απέτυχε — διατηρήθηκε η προηγούμενη έκδοση.
- Η ενημέρωση διαρκεί περισσότερο από το συνηθισμένο. Θα ολοκληρωθεί στο παρασκήνιο — ελέγξτε την έκδοση σε λίγο.
Δεν ήταν δυνατή η έναρξη της ενημέρωσης. Δοκιμάστε ξανά.
+ Ο πίνακας ενημερώνεται στο παρασκήνιο.
+ Ενημέρωση πίνακα
+ Ακύρωση
+ Η ενημέρωση ολοκληρώνεται και δεν μπορεί να ακυρωθεί τώρα.
+ Δεν ήταν δυνατή η ακύρωση της ενημέρωσης. Θα συνεχιστεί.
Dashboard (REST API)
Πυρήνας συστήματος · πάντα εγκατεστημένος
Ο πίνακας ελέγχου είναι ο πυρήνας REST API της συσκευής (dash-node) — μια μικρή υπηρεσία Node στο 127.0.0.1:4000, που εξυπηρετείται από nginx και εποπτεύεται από pdsm. Τροφοδοτεί κάθε ενέργεια της βιβλιοθήκης στην εφαρμογή: περιήγηση και λήψη Βιβλίων, προσθήκη και διαγραφή περιεχομένου Wikipedia και ZIM, συνεδρίες Kolibri, Χάρτες και κατάσταση συστήματος. Σε αντίθεση με τα module περιεχομένου, δεν μπορείς να το εγκαταστήσεις ή να το αφαιρέσεις. Έρχεται με την εφαρμογή και είναι πάντα παρόν. Όταν είναι διαθέσιμη νεότερη έκδοση, μπορείς να το ξαναχτίσεις επιτόπου.
@@ -1245,6 +1249,7 @@
Μια επαναφορά είναι σε εξέλιξη. Περίμενε να ολοκληρωθεί.
Μια εγκατάσταση είναι σε εξέλιξη. Περίμενε να ολοκληρωθεί.
Μια λήψη είναι σε εξέλιξη. Περίμενε να ολοκληρωθεί.
+ Εκτελείται ενημέρωση του πίνακα. Περιμένετε να ολοκληρωθεί.
Εκκρεμείς λήψεις
Δεν υπάρχουν εκκρεμείς λήψεις.
Γίνεται λήψη τώρα
diff --git a/controller/app/src/main/res/values-es/strings.xml b/controller/app/src/main/res/values-es/strings.xml
index 81b276521..8f30400c7 100644
--- a/controller/app/src/main/res/values-es/strings.xml
+++ b/controller/app/src/main/res/values-es/strings.xml
@@ -1191,14 +1191,18 @@
Núcleo REST · instalado v%1$s
Reconstruir
¿Reconstruir el núcleo REST?
- Actualiza dash-node a la última versión. El servidor queda brevemente no disponible mientras se reconstruye; no salgas de esta pantalla hasta que termine.
+ Actualiza dash-node a la última versión. Se ejecuta en segundo plano; el servidor queda brevemente no disponible mientras se reconstruye.
La reconstrucción necesita conexión a internet.
Actualizando el panel
Actualizando en el sitio… el panel se reinicia brevemente.
Panel actualizado.
La actualización falló — se conservó la versión anterior.
- La actualización está tardando más de lo normal. Terminará en segundo plano — revisa la versión en un momento.
No se pudo iniciar la actualización. Inténtalo de nuevo.
+ Actualizando el panel en segundo plano.
+ Actualización del panel
+ Cancelar
+ La actualización está terminando y ya no se puede cancelar.
+ No se pudo cancelar la actualización. Seguirá en ejecución.
Dashboard (REST API)
Núcleo del sistema · siempre instalado
El panel es el núcleo de la REST API del box (dash-node): un pequeño servicio Node en 127.0.0.1:4000, servido por nginx y supervisado por pdsm. Impulsa cada acción de la biblioteca en la app: explorar y descargar Libros, agregar y eliminar contenido de Wikipedia y ZIM, sesiones de Kolibri, Mapas y estado del sistema. A diferencia de los módulos de contenido, no puedes instalarlo ni quitarlo. Viene con la app y siempre está presente. Cuando hay una compilación más nueva, puedes reconstruirlo en el lugar.
@@ -1314,6 +1318,7 @@
Hay una restauración en progreso. Espera a que termine.
Hay una instalación en progreso. Espera a que termine.
Hay una descarga en progreso. Espera a que termine.
+ Hay una actualización del panel en curso. Espera a que termine.
Descargas pendientes
No hay descargas pendientes.
Se está descargando algo ahora
diff --git a/controller/app/src/main/res/values-fa/strings.xml b/controller/app/src/main/res/values-fa/strings.xml
index 040b0806a..a61a398f6 100644
--- a/controller/app/src/main/res/values-fa/strings.xml
+++ b/controller/app/src/main/res/values-fa/strings.xml
@@ -1115,14 +1115,18 @@
هستهٔ REST · نصبشده v%1$s
بازسازی
هستهٔ REST بازسازی شود؟
- dash-node را به آخرین نسخه بهروزرسانی میکند. سرور در حین بازسازی برای مدت کوتاهی در دسترس نیست؛ تا پایان کار این صفحه را ترک نکنید.
+ dash-node را به آخرین نسخه بهروزرسانی میکند. در پسزمینه اجرا میشود؛ سرور هنگام بازسازی برای مدت کوتاهی در دسترس نیست.
بازسازی به اتصال اینترنت نیاز دارد.
در حال بهروزرسانی داشبورد
بهروزرسانی در محل… داشبورد برای لحظهای دوباره راهاندازی میشود.
داشبورد بهروزرسانی شد.
بهروزرسانی ناموفق بود — نسخهٔ قبلی حفظ شد.
- بهروزرسانی بیشتر از حد معمول طول میکشد. در پسزمینه تمام میشود — کمی بعد نسخه را بررسی کنید.
شروع بهروزرسانی ممکن نشد. دوباره تلاش کنید.
+ داشبورد در پسزمینه بهروزرسانی میشود.
+ بهروزرسانی داشبورد
+ لغو
+ بهروزرسانی در حال پایان است و اکنون نمیتوان آن را لغو کرد.
+ لغو بهروزرسانی ممکن نشد. ادامه خواهد یافت.
Dashboard (REST API)
هستهٔ سیستم · همیشه نصبشده
داشبورد هستهٔ REST API دستگاه (dash-node) است — یک سرویس کوچک Node روی 127.0.0.1:4000 که با nginx ارائه و با pdsm نظارت میشود. هر عملیات کتابخانه در برنامه را پیش میبرد: مرور و دانلود کتابها، افزودن و حذف محتوای Wikipedia و ZIM، جلسات Kolibri، نقشهها و وضعیت سیستم. برخلاف ماژولهای محتوا، نمیتوانید آن را نصب یا حذف کنید. همراه برنامه میآید و همیشه حاضر است. وقتی نسخهٔ جدیدتری موجود باشد، میتوانید آن را در همانجا بازسازی کنید.
@@ -1238,6 +1242,7 @@
یک بازیابی در حال انجام است. لطفاً تا پایان صبر کنید.
یک نصب در حال انجام است. لطفاً تا پایان صبر کنید.
یک دانلود در حال انجام است. لطفاً تا پایان صبر کنید.
+ بهروزرسانی داشبورد در حال انجام است. لطفاً تا پایان صبر کنید.
دانلودهای در انتظار
دانلودی در انتظار نیست.
چیزی در حال دانلود است
diff --git a/controller/app/src/main/res/values-fr/strings.xml b/controller/app/src/main/res/values-fr/strings.xml
index d6bb61a2c..5bc1d8633 100644
--- a/controller/app/src/main/res/values-fr/strings.xml
+++ b/controller/app/src/main/res/values-fr/strings.xml
@@ -1202,14 +1202,18 @@
Cœur REST · v%1$s installée
Reconstruire
Reconstruire le cœur REST ?
- Met à jour dash-node vers la dernière version. Le serveur est brièvement indisponible pendant la reconstruction ; ne quittez pas cet écran avant la fin.
+ Met à jour dash-node vers la dernière version. La mise à jour s\'exécute en arrière-plan ; le serveur est brièvement indisponible pendant la reconstruction.
La reconstruction nécessite une connexion internet.
Mise à jour du tableau de bord
Mise à jour sur place… le tableau de bord redémarre brièvement.
Tableau de bord mis à jour.
Échec de la mise à jour — la version précédente a été conservée.
- La mise à jour prend plus de temps que d\'habitude. La mise à jour se terminera en arrière-plan — vérifiez la version dans un instant.
Impossible de démarrer la mise à jour. Réessayez.
+ Mise à jour du tableau de bord en arrière-plan.
+ Mise à jour du tableau de bord
+ Annuler
+ La mise à jour se termine et ne peut plus être annulée.
+ Impossible d\'annuler la mise à jour. Elle va continuer.
Dashboard (REST API)
Cœur du système · toujours installé
Le tableau de bord est le cœur de l\'API REST du box (dash-node) : un petit service Node sur 127.0.0.1:4000, servi par nginx et supervisé par pdsm. Il alimente chaque action de la bibliothèque dans l\'application : parcourir et télécharger des Livres, ajouter et supprimer du contenu Wikipédia et ZIM, les sessions Kolibri, les Cartes et l\'état du système. Contrairement aux modules de contenu, vous ne pouvez pas l\'installer ni le supprimer. Il est fourni avec l\'application et toujours présent. Quand une version plus récente est disponible, vous pouvez le reconstruire sur place.
@@ -1325,6 +1329,7 @@
Une restauration est en cours. Veuillez attendre qu\'elle se termine.
Une installation est en cours. Veuillez attendre qu\'elle se termine.
Un téléchargement est en cours. Veuillez attendre qu\'il se termine.
+ Une mise à jour du tableau de bord est en cours. Veuillez patienter jusqu\'à la fin.
Téléchargements en attente
Aucun téléchargement en attente.
Un téléchargement est en cours
diff --git a/controller/app/src/main/res/values-gu/strings.xml b/controller/app/src/main/res/values-gu/strings.xml
index 088052da1..eb72bc0b2 100644
--- a/controller/app/src/main/res/values-gu/strings.xml
+++ b/controller/app/src/main/res/values-gu/strings.xml
@@ -1128,14 +1128,18 @@
REST કોર · સ્થાપિત v%1$s
ફરીથી બનાવો
REST કોર ફરીથી બનાવવો?
- dash-node ને નવીનતમ સંસ્કરણમાં અપડેટ કરે છે. ફરીથી બનાવતી વખતે સર્વર થોડા સમય માટે અનુપલબ્ધ રહે છે; પૂર્ણ ન થાય ત્યાં સુધી આ સ્ક્રીન છોડશો નહીં.
+ dash-node ને નવીનતમ સંસ્કરણમાં અપડેટ કરે છે. તે પૃષ્ઠભૂમિમાં ચાલે છે; પુનર્નિર્માણ દરમિયાન સર્વર થોડા સમય માટે અનુપલબ્ધ રહે છે.
ફરીથી બનાવવા માટે ઇન્ટરનેટ કનેક્શન જરૂરી છે.
ડેશબોર્ડ અપડેટ થઈ રહ્યું છે
સ્થળ પર જ અપડેટ થઈ રહ્યું છે… ડેશબોર્ડ થોડી વાર માટે રીસ્ટાર્ટ થશે.
ડેશબોર્ડ અપડેટ થયું.
અપડેટ નિષ્ફળ — અગાઉની આવૃત્તિ જાળવી રાખી.
- અપડેટ સામાન્ય કરતાં વધુ સમય લઈ રહ્યું છે। તે પૃષ્ઠભૂમિમાં પૂર્ણ થશે — થોડી વારમાં આવૃત્તિ તપાસો.
અપડેટ શરૂ કરી શકાયું નહીં. ફરી પ્રયાસ કરો.
+ ડેશબોર્ડ પૃષ્ઠભૂમિમાં અપડેટ થઈ રહ્યું છે.
+ ડેશબોર્ડ અપડેટ
+ રદ કરો
+ અપડેટ પૂર્ણ થઈ રહ્યું છે અને હવે રદ કરી શકાતું નથી.
+ અપડેટ રદ કરી શકાયું નથી. તે ચાલુ રહેશે.
Dashboard (REST API)
સિસ્ટમ કોર · હંમેશા સ્થાપિત
ડૅશબોર્ડ એ બોક્સનું REST API કોર (dash-node) છે — 127.0.0.1:4000 પર એક નાની Node સેવા, જે nginx દ્વારા પીરસાય છે અને pdsm દ્વારા દેખરેખ થાય છે. તે ઍપમાં દરેક લાઇબ્રેરી ક્રિયા ચલાવે છે: પુસ્તકો બ્રાઉઝ અને ડાઉનલોડ, Wikipedia અને ZIM સામગ્રી ઉમેરવી અને કાઢવી, Kolibri સત્રો, નકશા અને સિસ્ટમ સ્થિતિ. સામગ્રી મોડ્યુલથી વિપરીત, તમે તેને ઇન્સ્ટોલ કે દૂર કરી શકતા નથી. તે ઍપ સાથે આવે છે અને હંમેશા હાજર હોય છે. જ્યારે નવો બિલ્ડ ઉપલબ્ધ હોય, ત્યારે તમે તેને એ જ સ્થાને ફરીથી બનાવી શકો છો.
@@ -1251,6 +1255,7 @@
એક પુનઃસ્થાપન ચાલુ છે. કૃપા કરી પૂર્ણ થાય ત્યાં સુધી રાહ જુઓ.
એક ઇન્સ્ટોલ ચાલુ છે. કૃપા કરી પૂર્ણ થાય ત્યાં સુધી રાહ જુઓ.
એક ડાઉનલોડ ચાલુ છે. કૃપા કરી પૂર્ણ થાય ત્યાં સુધી રાહ જુઓ.
+ ડેશબોર્ડ અપડેટ ચાલી રહ્યું છે. પૂર્ણ થાય ત્યાં સુધી રાહ જુઓ.
બાકી ડાઉનલોડ્સ
કોઈ બાકી ડાઉનલોડ નથી.
અત્યારે કંઈક ડાઉનલોડ થઈ રહ્યું છે
diff --git a/controller/app/src/main/res/values-hi/strings.xml b/controller/app/src/main/res/values-hi/strings.xml
index f71c70174..17c63e66f 100644
--- a/controller/app/src/main/res/values-hi/strings.xml
+++ b/controller/app/src/main/res/values-hi/strings.xml
@@ -1192,14 +1192,18 @@
REST कोर · स्थापित v%1$s
फिर से बनाएँ
REST कोर फिर से बनाएँ?
- dash-node को नवीनतम संस्करण में अपडेट करता है. फिर से बनाने के दौरान सर्वर थोड़ी देर के लिए अनुपलब्ध रहता है; समाप्त होने तक यह स्क्रीन न छोड़ें.
+ dash-node को नवीनतम संस्करण में अपडेट करता है। यह पृष्ठभूमि में चलता है; पुनर्निर्माण के दौरान सर्वर कुछ समय के लिए अनुपलब्ध रहता है।
फिर से बनाने के लिए इंटरनेट कनेक्शन आवश्यक है.
डैशबोर्ड अपडेट हो रहा है
यहीं अपडेट हो रहा है… डैशबोर्ड थोड़ी देर के लिए पुनः शुरू होगा।
डैशबोर्ड अपडेट हो गया।
अपडेट विफल — पिछला संस्करण रखा गया।
- अपडेट सामान्य से अधिक समय ले रहा है। यह पृष्ठभूमि में पूरा होगा — थोड़ी देर में संस्करण जांचें।
अपडेट शुरू नहीं हो सका। फिर से प्रयास करें।
+ डैशबोर्ड पृष्ठभूमि में अपडेट हो रहा है।
+ डैशबोर्ड अपडेट
+ रद्द करें
+ अपडेट पूरा हो रहा है और अब रद्द नहीं किया जा सकता।
+ अपडेट रद्द नहीं किया जा सका। यह चलता रहेगा।
Dashboard (REST API)
सिस्टम कोर · हमेशा स्थापित
डैशबोर्ड बॉक्स का REST API कोर (dash-node) है — 127.0.0.1:4000 पर एक छोटी Node सेवा, जो nginx द्वारा प्रस्तुत और pdsm द्वारा नियंत्रित होती है। यह ऐप में हर लाइब्रेरी क्रिया को चलाता है: किताबें ब्राउज़ और डाउनलोड करना, Wikipedia और ZIM सामग्री जोड़ना और हटाना, Kolibri सत्र, मानचित्र और सिस्टम स्थिति। सामग्री मॉड्यूल के विपरीत, आप इसे इंस्टॉल या हटा नहीं सकते। यह ऐप के साथ आता है और हमेशा मौजूद रहता है। जब कोई नया बिल्ड उपलब्ध हो, तो आप इसे यथास्थान फिर से बना सकते हैं।
@@ -1315,6 +1319,7 @@
एक पुनर्स्थापना जारी है। कृपया इसके पूरा होने की प्रतीक्षा करें।
एक इंस्टॉल जारी है। कृपया इसके पूरा होने की प्रतीक्षा करें।
एक डाउनलोड जारी है। कृपया इसके पूरा होने की प्रतीक्षा करें।
+ डैशबोर्ड अपडेट चल रहा है। कृपया इसके पूरा होने तक प्रतीक्षा करें।
लंबित डाउनलोड
कोई लंबित डाउनलोड नहीं।
अभी कुछ डाउनलोड हो रहा है
diff --git a/controller/app/src/main/res/values-hu/strings.xml b/controller/app/src/main/res/values-hu/strings.xml
index bed04773e..c3be8b4b9 100644
--- a/controller/app/src/main/res/values-hu/strings.xml
+++ b/controller/app/src/main/res/values-hu/strings.xml
@@ -1115,14 +1115,18 @@
REST mag · telepítve v%1$s
Újraépítés
Újraépíti a REST magot?
- Frissíti a dash-node-ot a legújabb verzióra. A szerver az újraépítés alatt rövid ideig nem elérhető; ne hagyd el ezt a képernyőt, amíg be nem fejeződik.
+ A dash-node frissítése a legújabb verzióra. A háttérben fut; a szerver az újraépítés alatt rövid ideig nem érhető el.
Az újraépítéshez internetkapcsolat szükséges.
Irányítópult frissítése
Frissítés helyben… az irányítópult röviden újraindul.
Az irányítópult frissült.
A frissítés sikertelen — az előző verzió megmaradt.
- A frissítés a szokásosnál tovább tart. A háttérben befejeződik — nézze meg a verziót egy pillanat múlva.
A frissítést nem sikerült elindítani. Próbálja újra.
+ Az irányítópult frissítése a háttérben.
+ Irányítópult frissítése
+ Mégse
+ A frissítés befejeződik, és most nem szakítható meg.
+ A frissítést nem sikerült megszakítani. Tovább fut.
Dashboard (REST API)
Rendszermag · mindig telepítve
Az irányítópult a box REST API-magja (dash-node) — egy kis Node szolgáltatás a 127.0.0.1:4000 címen, nginx mögött, pdsm által felügyelve. Minden appon belüli könyvtárműveletet ez hajt: Könyvek böngészése és letöltése, Wikipédia- és ZIM-tartalom hozzáadása és törlése, Kolibri-munkamenetek, Térképek és rendszerállapot. A tartalommoduloktól eltérően nem telepítheted és nem távolíthatod el. Az appal érkezik, és mindig jelen van. Ha újabb build érhető el, helyben újraépítheted.
@@ -1238,6 +1242,7 @@
Visszaállítás van folyamatban. Kérlek, várd meg, amíg befejeződik.
Telepítés van folyamatban. Kérlek, várd meg, amíg befejeződik.
Letöltés van folyamatban. Kérlek, várd meg, amíg befejeződik.
+ Irányítópult-frissítés van folyamatban. Kérjük, várja meg, amíg befejeződik.
Függőben lévő letöltések
Nincs függőben lévő letöltés.
Most letöltődik valami
diff --git a/controller/app/src/main/res/values-in/strings.xml b/controller/app/src/main/res/values-in/strings.xml
index 580c98f49..923308243 100644
--- a/controller/app/src/main/res/values-in/strings.xml
+++ b/controller/app/src/main/res/values-in/strings.xml
@@ -1122,14 +1122,18 @@
Inti REST · terpasang v%1$s
Bangun ulang
Bangun ulang inti REST?
- Memperbarui dash-node ke versi terbaru. Server sebentar tidak tersedia saat dibangun ulang; jangan tinggalkan layar ini sampai selesai.
+ Memperbarui dash-node ke versi terbaru. Berjalan di latar belakang; server sebentar tidak tersedia saat membangun ulang.
Membangun ulang memerlukan koneksi internet.
Memperbarui dasbor
Memperbarui di tempat… dasbor dimulai ulang sebentar.
Dasbor diperbarui.
Pembaruan gagal — versi sebelumnya dipertahankan.
- Pembaruan memakan waktu lebih lama dari biasanya. Akan selesai di latar belakang — periksa versinya sebentar lagi.
Tidak dapat memulai pembaruan. Coba lagi.
+ Memperbarui dasbor di latar belakang.
+ Pembaruan dasbor
+ Batal
+ Pembaruan sedang selesai dan tidak dapat dibatalkan sekarang.
+ Tidak dapat membatalkan pembaruan. Pembaruan akan terus berjalan.
Dashboard (REST API)
Inti sistem · selalu terpasang
Dasbor adalah inti REST API perangkat (dash-node) — layanan Node kecil di 127.0.0.1:4000, disajikan oleh nginx dan diawasi oleh pdsm. Ini menjalankan setiap tindakan pustaka di aplikasi: menjelajahi dan mengunduh Buku, menambah dan menghapus konten Wikipedia dan ZIM, sesi Kolibri, Peta, dan status sistem. Berbeda dengan modul konten, kamu tidak bisa memasang atau menghapusnya. Ini disertakan bersama aplikasi dan selalu ada. Saat build lebih baru tersedia, kamu bisa membangunnya ulang di tempat.
@@ -1245,6 +1249,7 @@
Pemulihan sedang berlangsung. Harap tunggu sampai selesai.
Pemasangan sedang berlangsung. Harap tunggu sampai selesai.
Unduhan sedang berlangsung. Harap tunggu sampai selesai.
+ Pembaruan dasbor sedang berlangsung. Harap tunggu hingga selesai.
Unduhan tertunda
Tidak ada unduhan tertunda.
Sesuatu sedang diunduh sekarang
diff --git a/controller/app/src/main/res/values-it/strings.xml b/controller/app/src/main/res/values-it/strings.xml
index bba8137ae..3704f6396 100644
--- a/controller/app/src/main/res/values-it/strings.xml
+++ b/controller/app/src/main/res/values-it/strings.xml
@@ -1115,14 +1115,18 @@
Nucleo REST · installato v%1$s
Ricostruisci
Ricostruire il nucleo REST?
- Aggiorna dash-node all\'ultima versione. Il server è brevemente non disponibile durante la ricostruzione; non lasciare questa schermata finché non termina.
+ Aggiorna dash-node all\'ultima versione. Viene eseguito in background; il server è brevemente non disponibile durante la ricostruzione.
La ricostruzione richiede una connessione a internet.
Aggiornamento della dashboard
Aggiornamento sul posto… la dashboard si riavvia brevemente.
Dashboard aggiornata.
Aggiornamento non riuscito — è stata mantenuta la versione precedente.
- L\'aggiornamento sta impiegando più del solito. Terminerà in background — controlla la versione tra un momento.
Impossibile avviare l\'aggiornamento. Riprova.
+ Aggiornamento della dashboard in background.
+ Aggiornamento della dashboard
+ Annulla
+ L\'aggiornamento sta terminando e non può essere annullato ora.
+ Impossibile annullare l\'aggiornamento. Continuerà.
Dashboard (REST API)
Nucleo di sistema · sempre installato
La dashboard è il cuore dell\'API REST del box (dash-node): un piccolo servizio Node su 127.0.0.1:4000, servito da nginx e supervisionato da pdsm. Alimenta ogni azione della libreria nell\'app: sfogliare e scaricare Libri, aggiungere ed eliminare contenuti di Wikipedia e ZIM, sessioni Kolibri, Mappe e stato del sistema. A differenza dei moduli di contenuto, non puoi installarla né rimuoverla. È inclusa nell\'app ed è sempre presente. Quando è disponibile una build più recente, puoi ricostruirla sul posto.
@@ -1238,6 +1242,7 @@
È in corso un ripristino. Attendi che finisca.
È in corso un\'installazione. Attendi che finisca.
È in corso un download. Attendi che finisca.
+ È in corso un aggiornamento della dashboard. Attendi il completamento.
Download in sospeso
Nessun download in sospeso.
È in corso un download
diff --git a/controller/app/src/main/res/values-ja/strings.xml b/controller/app/src/main/res/values-ja/strings.xml
index 381dba6fc..9deb712bd 100644
--- a/controller/app/src/main/res/values-ja/strings.xml
+++ b/controller/app/src/main/res/values-ja/strings.xml
@@ -1116,14 +1116,18 @@
REST コア · インストール済み v%1$s
再構築
REST コアを再構築しますか?
- dash-node を最新バージョンに更新します。再構築中はサーバーが一時的に利用できなくなります。完了するまでこの画面を離れないでください。
+ dash-node を最新バージョンに更新します。バックグラウンドで実行され、再構築中はサーバーが一時的に利用できなくなります。
再構築にはその後、方法を選びます:ダウンロード、または別の端末からコピー。
ダッシュボードを更新中
その場で更新中… ダッシュボードが少しの間再起動します。
ダッシュボードを更新しました。
更新に失敗しました — 以前のバージョンを維持しました。
- 更新に通常より時間がかかっています。バックグラウンドで完了します — 少し後にバージョンを確認してください。
更新を開始できませんでした。もう一度お試しください。
+ ダッシュボードをバックグラウンドで更新しています。
+ ダッシュボードの更新
+ キャンセル
+ 更新は完了間近で、今はキャンセルできません。
+ 更新をキャンセルできませんでした。続行されます。
Dashboard (REST API)
システムコア · 常にインストール済み
ダッシュボードは box の REST API コア(dash-node)です。127.0.0.1:4000 で動作する小さな Node サービスで、nginx が前面に立ち、pdsm が管理します。アプリ内のすべてのライブラリ操作を担います。書籍の閲覧とダウンロード、Wikipedia と ZIM コンテンツの追加と削除、Kolibri セッション、地図、システム状態です。コンテンツモジュールとは異なり、インストールや削除はできません。アプリに付属し、常に存在します。新しいビルドが利用可能になると、その場で再ビルドできます。
@@ -1239,6 +1243,7 @@
復元が進行中です。完了までお待ちください。
インストールが進行中です。完了までお待ちください。
ダウンロードが進行中です。完了までお待ちください。
+ ダッシュボードの更新が進行中です。完了までお待ちください。
保留中のダウンロード
保留中のダウンロードはありません。
現在ダウンロード中です
diff --git a/controller/app/src/main/res/values-ko/strings.xml b/controller/app/src/main/res/values-ko/strings.xml
index 8c49048cb..5c53ea75e 100644
--- a/controller/app/src/main/res/values-ko/strings.xml
+++ b/controller/app/src/main/res/values-ko/strings.xml
@@ -1116,14 +1116,18 @@
REST 코어 · 설치됨 v%1$s
재빌드
REST 코어를 재빌드할까요?
- dash-node를 최신 버전으로 업데이트합니다. 재빌드하는 동안 서버를 잠시 사용할 수 없습니다. 완료될 때까지 이 화면을 떠나지 마세요.
+ dash-node를 최신 버전으로 업데이트합니다. 백그라운드에서 실행되며, 다시 빌드하는 동안 서버를 잠시 사용할 수 없습니다.
재빌드하려면 인터넷 연결이 필요합니다.
대시보드 업데이트 중
제자리에서 업데이트 중… 대시보드가 잠시 다시 시작됩니다.
대시보드가 업데이트되었습니다.
업데이트 실패 — 이전 버전을 유지했습니다.
- 업데이트가 평소보다 오래 걸리고 있습니다. 백그라운드에서 완료됩니다 — 잠시 후 버전을 확인하세요.
업데이트를 시작할 수 없습니다. 다시 시도하세요.
+ 백그라운드에서 대시보드를 업데이트하는 중입니다.
+ 대시보드 업데이트
+ 취소
+ 업데이트가 마무리되고 있어 지금은 취소할 수 없습니다.
+ 업데이트를 취소하지 못했습니다. 계속 실행됩니다.
Dashboard (REST API)
시스템 코어 · 항상 설치됨
대시보드는 박스의 REST API 코어(dash-node)입니다. 127.0.0.1:4000에서 실행되는 작은 Node 서비스로, nginx가 앞단을 맡고 pdsm이 관리합니다. 앱 내 모든 라이브러리 작업을 구동합니다. 책 탐색 및 다운로드, Wikipedia 및 ZIM 콘텐츠 추가 및 삭제, Kolibri 세션, 지도, 시스템 상태입니다. 콘텐츠 모듈과 달리 설치하거나 제거할 수 없습니다. 앱과 함께 제공되며 항상 존재합니다. 더 새로운 빌드가 있으면 그 자리에서 다시 빌드할 수 있습니다.
@@ -1239,6 +1243,7 @@
복원이 진행 중입니다. 완료될 때까지 기다리세요.
설치가 진행 중입니다. 완료될 때까지 기다리세요.
다운로드가 진행 중입니다. 완료될 때까지 기다리세요.
+ 대시보드 업데이트가 진행 중입니다. 완료될 때까지 기다려 주세요.
대기 중인 다운로드
대기 중인 다운로드가 없습니다.
지금 다운로드 중입니다
diff --git a/controller/app/src/main/res/values-lt/strings.xml b/controller/app/src/main/res/values-lt/strings.xml
index ee9ab8c89..6a3275b59 100644
--- a/controller/app/src/main/res/values-lt/strings.xml
+++ b/controller/app/src/main/res/values-lt/strings.xml
@@ -1132,14 +1132,18 @@
REST branduolys · įdiegta v%1$s
Atkurti
Atkurti REST branduolį?
- Atnaujina dash-node į naujausią versiją. Atkūrimo metu serveris trumpam nepasiekiamas; nepalikite šio ekrano, kol nebus baigta.
+ Atnaujina dash-node į naujausią versiją. Vykdoma fone; perkuriant serveris trumpam nepasiekiamas.
Atkūrimui reikia interneto ryšio.
Atnaujinama sąsaja
Atnaujinama vietoje… sąsaja trumpam paleidžiama iš naujo.
Sąsaja atnaujinta.
Atnaujinti nepavyko — palikta ankstesnė versija.
- Atnaujinimas trunka ilgiau nei įprastai. Bus baigta fone — netrukus patikrinkite versiją.
Nepavyko pradėti atnaujinimo. Bandykite dar kartą.
+ Skydelis atnaujinamas fone.
+ Skydelio atnaujinimas
+ Atšaukti
+ Atnaujinimas baigiamas ir dabar jo atšaukti negalima.
+ Nepavyko atšaukti atnaujinimo. Jis tęsis.
Dashboard (REST API)
Sistemos branduolys · visada įdiegtas
Skydelis yra įrenginio REST API branduolys (dash-node) — maža „Node“ paslauga adresu 127.0.0.1:4000, aptarnaujama „nginx“ ir prižiūrima „pdsm“. Ji maitina kiekvieną bibliotekos veiksmą programoje: Knygų naršymą ir atsisiuntimą, „Wikipedia“ ir ZIM turinio pridėjimą bei šalinimą, „Kolibri“ seansus, Žemėlapius ir sistemos būseną. Skirtingai nei turinio moduliai, jos negalima įdiegti ar pašalinti. Ji pateikiama su programa ir visada yra. Kai prieinama naujesnė versija, gali ją perkurti vietoje.
@@ -1255,6 +1259,7 @@
Vykdomas atkūrimas. Palaukite, kol baigsis.
Vykdomas diegimas. Palaukite, kol baigsis.
Vykdomas atsisiuntimas. Palaukite, kol baigsis.
+ Vykdomas skydelio atnaujinimas. Palaukite, kol jis bus baigtas.
Laukiantys atsisiuntimai
Nėra laukiančių atsisiuntimų.
Šiuo metu kažkas atsisiunčiama
diff --git a/controller/app/src/main/res/values-nl/strings.xml b/controller/app/src/main/res/values-nl/strings.xml
index 1f275af64..ec92aebce 100644
--- a/controller/app/src/main/res/values-nl/strings.xml
+++ b/controller/app/src/main/res/values-nl/strings.xml
@@ -1115,14 +1115,18 @@
REST-kern · geïnstalleerd v%1$s
Opnieuw opbouwen
REST-kern opnieuw opbouwen?
- Werkt dash-node bij naar de nieuwste versie. De server is tijdens het opnieuw opbouwen kort niet beschikbaar; verlaat dit scherm niet totdat het klaar is.
+ Werkt dash-node bij naar de nieuwste versie. Het draait op de achtergrond; de server is tijdens het opnieuw opbouwen kort niet beschikbaar.
Opnieuw opbouwen vereist een internetverbinding.
Dashboard bijwerken
Bijwerken ter plekke… het dashboard start kort opnieuw op.
Dashboard bijgewerkt.
Bijwerken mislukt — de vorige versie is behouden.
- De update duurt langer dan normaal. Het wordt op de achtergrond voltooid — controleer de versie zo meteen.
Kan de update niet starten. Probeer het opnieuw.
+ Dashboard wordt op de achtergrond bijgewerkt.
+ Dashboard-update
+ Annuleren
+ De update is bijna klaar en kan nu niet worden geannuleerd.
+ Kan de update niet annuleren. Deze blijft doorgaan.
Dashboard (REST API)
Systeemkern · altijd geïnstalleerd
Het dashboard is de REST API-kern van de box (dash-node): een kleine Node-service op 127.0.0.1:4000, geserveerd via nginx en beheerd door pdsm. Het drijft elke bibliotheekactie in de app aan: Boeken bekijken en downloaden, Wikipedia- en ZIM-inhoud toevoegen en verwijderen, Kolibri-sessies, Kaarten en systeemstatus. In tegenstelling tot de inhoudsmodules kun je het niet installeren of verwijderen. Het zit bij de app en is altijd aanwezig. Wanneer er een nieuwere build is, kun je het ter plekke opnieuw bouwen.
@@ -1238,6 +1242,7 @@
Er is een herstel bezig. Wacht tot dit klaar is.
Er is een installatie bezig. Wacht tot deze klaar is.
Er is een download bezig. Wacht tot deze klaar is.
+ Er is een dashboard-update bezig. Wacht tot deze klaar is.
Downloads in wachtrij
Geen downloads in wachtrij.
Er wordt nu iets gedownload
diff --git a/controller/app/src/main/res/values-no/strings.xml b/controller/app/src/main/res/values-no/strings.xml
index ef3d1f024..6503bc98d 100644
--- a/controller/app/src/main/res/values-no/strings.xml
+++ b/controller/app/src/main/res/values-no/strings.xml
@@ -1125,14 +1125,18 @@
REST-kjerne · installert v%1$s
Bygg på nytt
Bygge REST-kjernen på nytt?
- Oppdaterer dash-node til nyeste versjon. Serveren er kort utilgjengelig mens den bygges på nytt; ikke forlat denne skjermen før den er ferdig.
+ Oppdaterer dash-node til den nyeste versjonen. Det kjøres i bakgrunnen; serveren er kort utilgjengelig mens den bygges på nytt.
Ombygging krever internettforbindelse.
Oppdaterer dashbordet
Oppdaterer på stedet… dashbordet starter kort på nytt.
Dashbordet er oppdatert.
Oppdateringen mislyktes — forrige versjon ble beholdt.
- Oppdateringen tar lengre tid enn vanlig. Det fullføres i bakgrunnen — sjekk versjonen om et øyeblikk.
Kunne ikke starte oppdateringen. Prøv igjen.
+ Oppdaterer dashbordet i bakgrunnen.
+ Dashbordoppdatering
+ Avbryt
+ Oppdateringen fullføres og kan ikke avbrytes nå.
+ Kunne ikke avbryte oppdateringen. Den fortsetter.
Dashboard (REST API)
Systemkjerne · alltid installert
Dashbordet er boksens REST API-kjerne (dash-node) — en liten Node-tjeneste på 127.0.0.1:4000, servert via nginx og overvåket av pdsm. Det driver hver bibliotekhandling i appen: bla i og laste ned Bøker, legge til og slette Wikipedia- og ZIM-innhold, Kolibri-økter, Kart og systemstatus. I motsetning til innholdsmodulene kan du ikke installere eller fjerne den. Den følger med appen og er alltid til stede. Når en nyere build er tilgjengelig, kan du bygge den på nytt på stedet.
@@ -1248,6 +1252,7 @@
En gjenoppretting pågår. Vent til den er ferdig.
En installasjon pågår. Vent til den er ferdig.
En nedlasting pågår. Vent til den er ferdig.
+ En dashbordoppdatering pågår. Vent til den er ferdig.
Ventende nedlastinger
Ingen ventende nedlastinger.
Noe lastes ned nå
diff --git a/controller/app/src/main/res/values-pl/strings.xml b/controller/app/src/main/res/values-pl/strings.xml
index 97a2c097f..19c051b08 100644
--- a/controller/app/src/main/res/values-pl/strings.xml
+++ b/controller/app/src/main/res/values-pl/strings.xml
@@ -1125,14 +1125,18 @@
Rdzeń REST · zainstalowano v%1$s
Przebuduj
Przebudować rdzeń REST?
- Aktualizuje dash-node do najnowszej wersji. Serwer jest przez chwilę niedostępny podczas przebudowy; nie opuszczaj tego ekranu, dopóki się nie zakończy.
+ Aktualizuje dash-node do najnowszej wersji. Działa w tle; serwer jest przez chwilę niedostępny podczas przebudowy.
Przebudowa wymaga połączenia z internetem.
Aktualizowanie panelu
Aktualizacja w miejscu… panel na chwilę się zrestartuje.
Panel zaktualizowany.
Aktualizacja nie powiodła się — zachowano poprzednią wersję.
- Aktualizacja trwa dłużej niż zwykle. Zakończy się w tle — sprawdź wersję za chwilę.
Nie można rozpocząć aktualizacji. Spróbuj ponownie.
+ Aktualizowanie pulpitu w tle.
+ Aktualizacja pulpitu
+ Anuluj
+ Aktualizacja się kończy i nie można jej teraz anulować.
+ Nie udało się anulować aktualizacji. Będzie kontynuowana.
Dashboard (REST API)
Rdzeń systemu · zawsze zainstalowany
Panel to rdzeń REST API urządzenia (dash-node) — mała usługa Node na 127.0.0.1:4000, obsługiwana przez nginx i nadzorowana przez pdsm. Zasila każdą akcję biblioteki w aplikacji: przeglądanie i pobieranie Książek, dodawanie i usuwanie treści Wikipedii i ZIM, sesje Kolibri, Mapy i stan systemu. W przeciwieństwie do modułów treści nie możesz go zainstalować ani usunąć. Jest dołączony do aplikacji i zawsze obecny. Gdy dostępna jest nowsza kompilacja, możesz go przebudować w miejscu.
@@ -1248,6 +1252,7 @@
Trwa przywracanie. Poczekaj na zakończenie.
Trwa instalacja. Poczekaj na zakończenie.
Trwa pobieranie. Poczekaj na zakończenie.
+ Trwa aktualizacja pulpitu. Poczekaj na jej zakończenie.
Oczekujące pobierania
Brak oczekujących pobierań.
Coś jest teraz pobierane
diff --git a/controller/app/src/main/res/values-pt/strings.xml b/controller/app/src/main/res/values-pt/strings.xml
index 4476739af..51f7fa8a4 100644
--- a/controller/app/src/main/res/values-pt/strings.xml
+++ b/controller/app/src/main/res/values-pt/strings.xml
@@ -1194,14 +1194,18 @@
Núcleo REST · instalado v%1$s
Recompilar
Recompilar o núcleo REST?
- Atualiza o dash-node para a versão mais recente. O servidor fica brevemente indisponível durante a recompilação; não saia desta tela até terminar.
+ Atualiza o dash-node para a versão mais recente. É executado em segundo plano; o servidor fica brevemente indisponível durante a recompilação.
A recompilação precisa de conexão com a internet.
A atualizar o painel
A atualizar no local… o painel reinicia brevemente.
Painel atualizado.
A atualização falhou — a versão anterior foi mantida.
- A atualização está a demorar mais do que o habitual. Vai terminar em segundo plano — verifique a versão daqui a pouco.
Não foi possível iniciar a atualização. Tente novamente.
+ A atualizar o painel em segundo plano.
+ Atualização do painel
+ Cancelar
+ A atualização está a terminar e já não pode ser cancelada.
+ Não foi possível cancelar a atualização. Vai continuar.
Dashboard (REST API)
Núcleo do sistema · sempre instalado
O painel é o núcleo da API REST do box (dash-node): um pequeno serviço Node em 127.0.0.1:4000, servido pelo nginx e supervisionado pelo pdsm. Alimenta todas as ações da biblioteca na app: explorar e transferir Livros, adicionar e eliminar conteúdo da Wikipédia e ZIM, sessões do Kolibri, Mapas e estado do sistema. Ao contrário dos módulos de conteúdo, não podes instalá-lo nem removê-lo. Vem com a app e está sempre presente. Quando há uma versão mais recente, podes recompilá-lo no local.
@@ -1317,6 +1321,7 @@
Está um restauro em curso. Aguarda que termine.
Está uma instalação em curso. Aguarda que termine.
Está uma transferência em curso. Aguarda que termine.
+ Há uma atualização do painel em curso. Aguarde que termine.
Downloads pendentes
Nenhum download pendente.
Algo está sendo baixado agora
diff --git a/controller/app/src/main/res/values-ro/strings.xml b/controller/app/src/main/res/values-ro/strings.xml
index 68331b4a4..f79c7d728 100644
--- a/controller/app/src/main/res/values-ro/strings.xml
+++ b/controller/app/src/main/res/values-ro/strings.xml
@@ -1115,14 +1115,18 @@
Nucleu REST · instalat v%1$s
Reconstruiește
Reconstruiești nucleul REST?
- Actualizează dash-node la cea mai recentă versiune. Serverul este indisponibil pentru scurt timp în timpul reconstruirii; nu părăsi acest ecran până nu se termină.
+ Actualizează dash-node la cea mai recentă versiune. Rulează în fundal; serverul este indisponibil pentru scurt timp în timpul reconstruirii.
Reconstruirea necesită o conexiune la internet.
Se actualizează panoul
Se actualizează pe loc… panoul repornește pentru scurt timp.
Panoul a fost actualizat.
Actualizarea a eșuat — a fost păstrată versiunea anterioară.
- Actualizarea durează mai mult decât de obicei. Se va finaliza în fundal — verificați versiunea într-un moment.
Actualizarea nu a putut porni. Încercați din nou.
+ Se actualizează panoul în fundal.
+ Actualizarea panoului
+ Anulează
+ Actualizarea se finalizează și nu mai poate fi anulată acum.
+ Actualizarea nu a putut fi anulată. Va continua.
Dashboard (REST API)
Nucleul sistemului · întotdeauna instalat
Panoul este nucleul API REST al box-ului (dash-node) — un mic serviciu Node pe 127.0.0.1:4000, servit prin nginx și supravegheat de pdsm. Alimentează fiecare acțiune a bibliotecii din aplicație: răsfoirea și descărcarea de Cărți, adăugarea și ștergerea de conținut Wikipedia și ZIM, sesiuni Kolibri, Hărți și starea sistemului. Spre deosebire de modulele de conținut, nu îl poți instala sau elimina. Vine cu aplicația și este mereu prezent. Când e disponibil un build mai nou, îl poți reconstrui pe loc.
@@ -1238,6 +1242,7 @@
O restaurare este în curs. Așteaptă să se termine.
O instalare este în curs. Așteaptă să se termine.
O descărcare este în curs. Așteaptă să se termine.
+ O actualizare a panoului este în curs. Așteptați să se finalizeze.
Descărcări în așteptare
Nicio descărcare în așteptare.
Se descarcă ceva acum
diff --git a/controller/app/src/main/res/values-ru-rRU/strings.xml b/controller/app/src/main/res/values-ru-rRU/strings.xml
index 8693c79bb..89ae5209e 100644
--- a/controller/app/src/main/res/values-ru-rRU/strings.xml
+++ b/controller/app/src/main/res/values-ru-rRU/strings.xml
@@ -1191,14 +1191,18 @@
Ядро REST · установлена v%1$s
Пересобрать
Пересобрать ядро REST?
- Обновляет dash-node до последней версии. Во время пересборки сервер ненадолго недоступен; не покидайте этот экран, пока она не завершится.
+ Обновляет dash-node до последней версии. Выполняется в фоне; сервер ненадолго недоступен во время пересборки.
Для пересборки нужно подключение к интернету.
Обновление панели
Обновление на месте… панель ненадолго перезапустится.
Панель обновлена.
Не удалось обновить — сохранена предыдущая версия.
- Обновление занимает больше времени, чем обычно. Завершится в фоне — проверьте версию через минуту.
Не удалось запустить обновление. Повторите попытку.
+ Панель обновляется в фоне.
+ Обновление панели
+ Отмена
+ Обновление завершается, отменить его сейчас нельзя.
+ Не удалось отменить обновление. Оно продолжит выполняться.
Dashboard (REST API)
Ядро системы · всегда установлено
Панель — это ядро REST API устройства (dash-node): небольшая служба Node на 127.0.0.1:4000, обслуживаемая nginx и управляемая pdsm. Она обеспечивает все действия библиотеки в приложении: просмотр и загрузку Книг, добавление и удаление контента Wikipedia и ZIM, сеансы Kolibri, Карты и статус системы. В отличие от модулей контента, её нельзя установить или удалить. Она поставляется с приложением и всегда присутствует. Когда доступна новая сборка, вы можете пересобрать её на месте.
@@ -1314,6 +1318,7 @@
Выполняется восстановление. Дождитесь завершения.
Выполняется установка. Дождитесь завершения.
Выполняется загрузка. Дождитесь завершения.
+ Выполняется обновление панели. Дождитесь завершения.
Ожидающие загрузки
Нет ожидающих загрузок.
Сейчас что-то загружается
diff --git a/controller/app/src/main/res/values-sk/strings.xml b/controller/app/src/main/res/values-sk/strings.xml
index 6e6812883..b15ae8421 100644
--- a/controller/app/src/main/res/values-sk/strings.xml
+++ b/controller/app/src/main/res/values-sk/strings.xml
@@ -1122,14 +1122,18 @@
Jadro REST · nainštalované v%1$s
Prestavať
Prestavať jadro REST?
- Aktualizuje dash-node na najnovšiu verziu. Server je počas prestavby krátko nedostupný; neopúšťajte túto obrazovku, kým sa neskončí.
+ Aktualizuje dash-node na najnovšiu verziu. Beží na pozadí; server je počas opätovného zostavovania krátko nedostupný.
Prestavba vyžaduje pripojenie na internet.
Aktualizácia nástenky
Aktualizácia na mieste… nástenka sa nakrátko reštartuje.
Nástenka je aktualizovaná.
Aktualizácia zlyhala — ponechala sa predchádzajúca verzia.
- Aktualizácia trvá dlhšie ako zvyčajne. Dokončí sa na pozadí — o chvíľu skontrolujte verziu.
Aktualizáciu sa nepodarilo spustiť. Skúste to znova.
+ Panel sa aktualizuje na pozadí.
+ Aktualizácia panela
+ Zrušiť
+ Aktualizácia sa dokončuje a teraz ju nemožno zrušiť.
+ Aktualizáciu sa nepodarilo zrušiť. Bude pokračovať.
Dashboard (REST API)
Jadro systému · vždy nainštalované
Ovládací panel je jadro REST API zariadenia (dash-node) — malá služba Node na 127.0.0.1:4000, obsluhovaná cez nginx a spravovaná pomocou pdsm. Poháňa každú akciu knižnice v aplikácii: prehliadanie a sťahovanie Kníh, pridávanie a mazanie obsahu Wikipédie a ZIM, relácie Kolibri, Mapy a stav systému. Na rozdiel od modulov obsahu ho nemôžeš nainštalovať ani odstrániť. Je súčasťou aplikácie a je vždy prítomný. Keď je k dispozícii novšie zostavenie, môžeš ho na mieste znova zostaviť.
@@ -1245,6 +1249,7 @@
Prebieha obnovenie. Počkaj, kým sa dokončí.
Prebieha inštalácia. Počkaj, kým sa dokončí.
Prebieha sťahovanie. Počkaj, kým sa dokončí.
+ Prebieha aktualizácia panela. Počkajte, kým sa dokončí.
Čakajúce sťahovania
Žiadne čakajúce sťahovania.
Práve sa niečo sťahuje
diff --git a/controller/app/src/main/res/values-sr/strings.xml b/controller/app/src/main/res/values-sr/strings.xml
index c48190a65..46adb6d45 100644
--- a/controller/app/src/main/res/values-sr/strings.xml
+++ b/controller/app/src/main/res/values-sr/strings.xml
@@ -1128,14 +1128,18 @@
REST језгро · инсталирана v%1$s
Поново изгради
Поново изградити REST језгро?
- Ажурира dash-node на најновију верзију. Сервер је накратко недоступан током поновне изградње; не напуштајте овај екран док се не заврши.
+ Ажурира dash-node на најновију верзију. Ради у позадини; сервер је накратко недоступан током поновне изградње.
Поновна изградња захтева интернет везу.
Ажурирање контролне табле
Ажурирање на лицу места… контролна табла ће се накратко поново покренути.
Контролна табла је ажурирана.
Ажурирање није успело — задржана је претходна верзија.
- Ажурирање траје дуже него обично. Завршиће се у позадини — проверите верзију за тренутак.
Ажурирање није могло да почне. Покушајте поново.
+ Контролна табла се ажурира у позадини.
+ Ажурирање контролне табле
+ Откажи
+ Ажурирање се завршава и не може се отказати сада.
+ Није могуће отказати ажурирање. Наставиће да ради.
Dashboard (REST API)
Системско језгро · увек инсталирано
Контролна табла је језгро REST API-ја уређаја (dash-node) — мала Node услуга на 127.0.0.1:4000, коју опслужује nginx и надзире pdsm. Покреће сваку радњу библиотеке у апликацији: прегледање и преузимање Књига, додавање и брисање садржаја Wikipedia и ZIM, Kolibri сесије, Мапе и статус система. За разлику од модула садржаја, не можеш је инсталирати ни уклонити. Долази уз апликацију и увек је присутна. Када је доступна новија верзија, можеш је поново изградити на месту.
@@ -1251,6 +1255,7 @@
Враћање је у току. Сачекај да се заврши.
Инсталација је у току. Сачекај да се заврши.
Преузимање је у току. Сачекај да се заврши.
+ У току је ажурирање контролне табле. Сачекајте да се заврши.
Преузимања на чекању
Нема преузимања на чекању.
Нешто се сада преузима
diff --git a/controller/app/src/main/res/values-sw/strings.xml b/controller/app/src/main/res/values-sw/strings.xml
index 7b9866730..5e27f12cc 100644
--- a/controller/app/src/main/res/values-sw/strings.xml
+++ b/controller/app/src/main/res/values-sw/strings.xml
@@ -1135,14 +1135,18 @@
Kiini cha REST · imesakinishwa v%1$s
Jenga upya
Ujenge upya kiini cha REST?
- Inasasisha dash-node hadi toleo jipya zaidi. Seva haipatikani kwa muda mfupi wakati wa kujengwa upya; usiondoke skrini hii hadi ikamilike.
+ Inasasisha dash-node hadi toleo la hivi karibuni. Inaendeshwa chinichini; seva haipatikani kwa muda mfupi wakati wa kujengwa upya.
Kujenga upya kunahitaji muunganisho wa intaneti.
Inasasisha dashibodi
Inasasisha papo hapo… dashibodi itaanza upya kwa muda mfupi.
Dashibodi imesasishwa.
Sasisho limeshindwa — toleo la awali limehifadhiwa.
- Sasisho linachukua muda mrefu kuliko kawaida. Itakamilika chinichini — angalia toleo baada ya muda mfupi.
Imeshindwa kuanzisha sasisho. Jaribu tena.
+ Inasasisha dashibodi chinichini.
+ Sasisho la dashibodi
+ Ghairi
+ Sasisho linakamilika na haliwezi kughairiwa sasa.
+ Imeshindwa kughairi sasisho. Litaendelea kufanya kazi.
Dashboard (REST API)
Kiini cha mfumo · kimesakinishwa daima
Dashibodi ni kiini cha REST API cha kifaa (dash-node) — huduma ndogo ya Node kwenye 127.0.0.1:4000, inayotolewa na nginx na kusimamiwa na pdsm. Inaendesha kila kitendo cha maktaba ndani ya programu: kuvinjari na kupakua Vitabu, kuongeza na kufuta maudhui ya Wikipedia na ZIM, vipindi vya Kolibri, Ramani na hali ya mfumo. Tofauti na moduli za maudhui, huwezi kuisakinisha wala kuiondoa. Inakuja na programu na ipo daima. Wakati toleo jipya linapatikana, unaweza kuijenga upya papo hapo.
@@ -1258,6 +1262,7 @@
Urejeshaji unaendelea. Tafadhali subiri ukamilike.
Usakinishaji unaendelea. Tafadhali subiri ukamilike.
Upakuaji unaendelea. Tafadhali subiri ukamilike.
+ Sasisho la dashibodi linaendelea. Tafadhali subiri limalizike.
Vipakuliwa vinavyosubiri
Hakuna vipakuliwa vinavyosubiri.
Kitu kinapakuliwa sasa
diff --git a/controller/app/src/main/res/values-ta/strings.xml b/controller/app/src/main/res/values-ta/strings.xml
index 35fd447d1..6991df675 100644
--- a/controller/app/src/main/res/values-ta/strings.xml
+++ b/controller/app/src/main/res/values-ta/strings.xml
@@ -1135,14 +1135,18 @@
REST கரு · நிறுவப்பட்டது v%1$s
மீண்டும் உருவாக்கு
REST கருவை மீண்டும் உருவாக்கவா?
- dash-node ஐ சமீபத்திய பதிப்பிற்குப் புதுப்பிக்கிறது. மீண்டும் உருவாக்கும்போது சேவையகம் சிறிது நேரம் கிடைக்காது; முடியும் வரை இந்தத் திரையை விட்டு வெளியேறாதீர்கள்.
+ dash-node ஐ சமீபத்திய பதிப்பிற்குப் புதுப்பிக்கிறது. இது பின்னணியில் இயங்குகிறது; மீண்டும் உருவாக்கப்படும்போது சேவையகம் சிறிது நேரம் கிடைக்காது.
மீண்டும் உருவாக்க இணைய இணைப்பு தேவை.
டாஷ்போர்டு புதுப்பிக்கப்படுகிறது
அப்படியே புதுப்பிக்கப்படுகிறது… டாஷ்போர்டு சிறிது நேரம் மறுதொடக்கம் செய்யப்படும்.
டாஷ்போர்டு புதுப்பிக்கப்பட்டது.
புதுப்பிப்பு தோல்வி — முந்தைய பதிப்பு வைத்திருக்கப்பட்டது.
- புதுப்பிப்பு வழக்கத்தை விட அதிக நேரம் எடுக்கிறது. இது பின்னணியில் முடியும் — சிறிது நேரத்தில் பதிப்பைச் சரிபார்க்கவும்.
புதுப்பிப்பைத் தொடங்க முடியவில்லை. மீண்டும் முயற்சிக்கவும்.
+ டாஷ்போர்டு பின்னணியில் புதுப்பிக்கப்படுகிறது.
+ டாஷ்போர்டு புதுப்பிப்பு
+ ரத்து
+ புதுப்பிப்பு முடிவடைகிறது, இப்போது ரத்து செய்ய முடியாது.
+ புதுப்பிப்பை ரத்து செய்ய முடியவில்லை. அது தொடர்ந்து இயங்கும்.
Dashboard (REST API)
கணினி கரு · எப்போதும் நிறுவப்பட்டது
டாஷ்போர்டு என்பது சாதனத்தின் REST API மையம் (dash-node) — 127.0.0.1:4000 இல் இயங்கும் ஒரு சிறிய Node சேவை, nginx வழங்குகிறது, pdsm மேற்பார்வையிடுகிறது. இது ஆப்பில் ஒவ்வொரு நூலகச் செயலையும் இயக்குகிறது: புத்தகங்களை உலாவுதல் மற்றும் பதிவிறக்குதல், Wikipedia மற்றும் ZIM உள்ளடக்கத்தைச் சேர்த்தல் மற்றும் நீக்குதல், Kolibri அமர்வுகள், வரைபடங்கள் மற்றும் சிஸ்டம் நிலை. உள்ளடக்கத் தொகுதிகளைப் போலல்லாமல், அதை நிறுவவோ அகற்றவோ முடியாது. அது ஆப்புடன் வருகிறது, எப்போதும் இருக்கும். புதிய பதிப்பு கிடைக்கும்போது, அதை அந்த இடத்திலேயே மீண்டும் உருவாக்கலாம்.
@@ -1258,6 +1262,7 @@
ஒரு மீட்டெடுப்பு நடந்துகொண்டிருக்கிறது. முடியும் வரை காத்திருக்கவும்.
ஒரு நிறுவல் நடந்துகொண்டிருக்கிறது. முடியும் வரை காத்திருக்கவும்.
ஒரு பதிவிறக்கம் நடந்துகொண்டிருக்கிறது. முடியும் வரை காத்திருக்கவும்.
+ டாஷ்போர்டு புதுப்பிப்பு நடைபெறுகிறது. முடியும் வரை காத்திருக்கவும்.
நிலுவையிலுள்ள பதிவிறக்கங்கள்
நிலுவையில் பதிவிறக்கங்கள் இல்லை.
இப்போது ஏதோ பதிவிறக்கப்படுகிறது
diff --git a/controller/app/src/main/res/values-tr/strings.xml b/controller/app/src/main/res/values-tr/strings.xml
index 6d679154a..dbe64dc07 100644
--- a/controller/app/src/main/res/values-tr/strings.xml
+++ b/controller/app/src/main/res/values-tr/strings.xml
@@ -1115,14 +1115,18 @@
REST çekirdeği · v%1$s yüklü
Yeniden oluştur
REST çekirdeği yeniden oluşturulsun mu?
- dash-node\'u en son sürüme günceller. Yeniden oluşturma sırasında sunucu kısa süre kullanılamaz; bitene kadar bu ekrandan ayrılmayın.
+ dash-node\'u en son sürüme günceller. Arka planda çalışır; yeniden oluşturulurken sunucu kısa süre kullanılamaz.
Yeniden oluşturma için internet bağlantısı gerekir.
Pano güncelleniyor
Yerinde güncelleniyor… pano kısa süre yeniden başlar.
Pano güncellendi.
Güncelleme başarısız — önceki sürüm korundu.
- Güncelleme her zamankinden uzun sürüyor. Arka planda tamamlanacak — birazdan sürümü kontrol edin.
Güncelleme başlatılamadı. Tekrar deneyin.
+ Kontrol paneli arka planda güncelleniyor.
+ Kontrol paneli güncellemesi
+ İptal
+ Güncelleme tamamlanıyor ve şimdi iptal edilemez.
+ Güncelleme iptal edilemedi. Çalışmaya devam edecek.
Dashboard (REST API)
Sistem çekirdeği · her zaman yüklü
Pano, cihazın REST API çekirdeğidir (dash-node) — 127.0.0.1:4000 üzerinde küçük bir Node hizmeti, nginx ile sunulur ve pdsm ile denetlenir. Uygulamadaki her kitaplık işlemini çalıştırır: Kitaplara göz atma ve indirme, Wikipedia ve ZIM içeriği ekleme ve silme, Kolibri oturumları, Haritalar ve sistem durumu. İçerik modüllerinin aksine onu kuramaz veya kaldıramazsın. Uygulamayla gelir ve her zaman vardır. Daha yeni bir sürüm mevcut olduğunda onu yerinde yeniden oluşturabilirsin.
@@ -1238,6 +1242,7 @@
Bir geri yükleme sürüyor. Lütfen bitmesini bekle.
Bir kurulum sürüyor. Lütfen bitmesini bekle.
Bir indirme sürüyor. Lütfen bitmesini bekle.
+ Bir kontrol paneli güncellemesi sürüyor. Lütfen bitmesini bekleyin.
Bekleyen indirmeler
Bekleyen indirme yok.
Şu anda bir şey indiriliyor
diff --git a/controller/app/src/main/res/values-uk/strings.xml b/controller/app/src/main/res/values-uk/strings.xml
index ea1c13bb1..19c86de64 100644
--- a/controller/app/src/main/res/values-uk/strings.xml
+++ b/controller/app/src/main/res/values-uk/strings.xml
@@ -1115,14 +1115,18 @@
Ядро REST · встановлено v%1$s
Перезібрати
Перезібрати ядро REST?
- Оновлює dash-node до найновішої версії. Під час перезбирання сервер ненадовго недоступний; не залишайте цей екран, доки воно не завершиться.
+ Оновлює dash-node до останньої версії. Виконується у фоні; сервер ненадовго недоступний під час перезбирання.
Для перезбирання потрібне з\'єднання з інтернетом.
Оновлення панелі
Оновлення на місці… панель ненадовго перезапуститься.
Панель оновлено.
Не вдалося оновити — збережено попередню версію.
- Оновлення триває довше, ніж зазвичай. Завершиться у фоні — перевірте версію за мить.
Не вдалося розпочати оновлення. Спробуйте ще раз.
+ Панель оновлюється у фоні.
+ Оновлення панелі
+ Скасувати
+ Оновлення завершується, його не можна скасувати зараз.
+ Не вдалося скасувати оновлення. Воно продовжуватиметься.
Dashboard (REST API)
Ядро системи · завжди встановлено
Панель — це ядро REST API пристрою (dash-node): невелика служба Node на 127.0.0.1:4000, що обслуговується nginx і керується pdsm. Вона забезпечує всі дії бібліотеки в застосунку: перегляд і завантаження Книг, додавання й видалення вмісту Wikipedia та ZIM, сеанси Kolibri, Карти й стан системи. На відміну від модулів вмісту, її не можна встановити чи видалити. Вона постачається із застосунком і завжди присутня. Коли доступна новіша збірка, ви можете перезібрати її на місці.
@@ -1238,6 +1242,7 @@
Виконується відновлення. Зачекайте на завершення.
Виконується встановлення. Зачекайте на завершення.
Виконується завантаження. Зачекайте на завершення.
+ Триває оновлення панелі. Зачекайте, доки воно завершиться.
Завантаження в черзі
Немає завантажень у черзі.
Зараз щось завантажується
diff --git a/controller/app/src/main/res/values-vi/strings.xml b/controller/app/src/main/res/values-vi/strings.xml
index e4534b4e2..f6d3097c6 100644
--- a/controller/app/src/main/res/values-vi/strings.xml
+++ b/controller/app/src/main/res/values-vi/strings.xml
@@ -1115,14 +1115,18 @@
Lõi REST · đã cài v%1$s
Xây dựng lại
Xây dựng lại lõi REST?
- Cập nhật dash-node lên phiên bản mới nhất. Máy chủ tạm thời không khả dụng trong khi xây dựng lại; đừng rời màn hình này cho đến khi hoàn tất.
+ Cập nhật dash-node lên phiên bản mới nhất. Nó chạy ở chế độ nền; máy chủ tạm thời không khả dụng trong khi xây dựng lại.
Việc xây dựng lại cần kết nối internet.
Đang cập nhật bảng điều khiển
Đang cập nhật tại chỗ… bảng điều khiển khởi động lại trong giây lát.
Đã cập nhật bảng điều khiển.
Cập nhật thất bại — phiên bản trước đã được giữ lại.
- Bản cập nhật đang lâu hơn bình thường. Nó sẽ hoàn tất ở chế độ nền — kiểm tra phiên bản trong giây lát.
Không thể bắt đầu cập nhật. Hãy thử lại.
+ Đang cập nhật bảng điều khiển ở chế độ nền.
+ Cập nhật bảng điều khiển
+ Hủy
+ Bản cập nhật đang hoàn tất và không thể hủy lúc này.
+ Không thể hủy bản cập nhật. Nó sẽ tiếp tục chạy.
Dashboard (REST API)
Lõi hệ thống · luôn được cài đặt
Bảng điều khiển là lõi REST API của thiết bị (dash-node) — một dịch vụ Node nhỏ tại 127.0.0.1:4000, được phục vụ qua nginx và giám sát bởi pdsm. Nó vận hành mọi thao tác thư viện trong ứng dụng: duyệt và tải Sách, thêm và xóa nội dung Wikipedia và ZIM, phiên Kolibri, Bản đồ và trạng thái hệ thống. Khác với các mô-đun nội dung, bạn không thể cài đặt hay gỡ nó. Nó đi kèm ứng dụng và luôn hiện diện. Khi có bản dựng mới hơn, bạn có thể dựng lại tại chỗ.
@@ -1238,6 +1242,7 @@
Đang khôi phục. Vui lòng đợi cho đến khi hoàn tất.
Đang cài đặt. Vui lòng đợi cho đến khi hoàn tất.
Đang tải xuống. Vui lòng đợi cho đến khi hoàn tất.
+ Đang cập nhật bảng điều khiển. Vui lòng đợi cho đến khi hoàn tất.
Tải xuống đang chờ
Không có tải xuống đang chờ.
Đang tải xuống một mục
diff --git a/controller/app/src/main/res/values-yo/strings.xml b/controller/app/src/main/res/values-yo/strings.xml
index 9237c7cec..29c618a07 100644
--- a/controller/app/src/main/res/values-yo/strings.xml
+++ b/controller/app/src/main/res/values-yo/strings.xml
@@ -1135,14 +1135,18 @@
Kókó REST · fi sori ẹrọ v%1$s
Tún kọ́
Tún kókó REST kọ́?
- Ó ń ṣe àfikún dash-node sí ẹ̀yà tuntun. Sáfà kì yóò sí fún ìgbà díẹ̀ nígbà tí ó ń tún kọ́; má ṣe kúrò ní ojú-iwé yìí títí yóò fi parí.
+ Ó ń mú dash-node dójú ìwọ̀n sí ẹ̀yà tuntun. Ó ń ṣiṣẹ́ ní ẹ̀yìn; olùpèsè kì yóò sí fún ìgbà díẹ̀ nígbà tí ó bá ń tún kọ́.
Àtúnkọ́ nílò ìsopọ̀ íntánẹ́ẹ̀tì.
Ń ṣàtúnṣe dáàṣíbọ́ọ̀dù
Ń ṣàtúnṣe níbí… dáàṣíbọ́ọ̀dù yóò tún bẹ̀rẹ̀ fún ìgbà díẹ̀.
A ti ṣàtúnṣe dáàṣíbọ́ọ̀dù.
Àtúnṣe kùnà — a pa ẹ̀yà tí ó ṣáájú mọ́.
- Ìmúdójúìwọ̀n náà ń gba àkókò ju ti àtẹ̀yìnwá lọ. Yóò parí ní ẹ̀yìn — ṣàyẹ̀wò ẹ̀yà náà láìpẹ́.
Kò lè bẹ̀rẹ̀ àtúnṣe. Gbìyànjú lẹ́ẹ̀kansi.
+ Ń mú dáṣíbọ̀ọ̀dù dójú ìwọ̀n ní ẹ̀yìn.
+ Ìmúdójúìwọ̀n dáṣíbọ̀ọ̀dù
+ Fagilee
+ Ìmúdójúìwọ̀n náà ń parí, kò sì ṣeé fagilee báyìí.
+ Kò ṣeé fagilee ìmúdójúìwọ̀n náà. Yóò máa bá a lọ.
Dashboard (REST API)
Kókó ètò · fi sori ẹrọ nígbà gbogbo
Dáṣíbọ́ọ̀dù ni kókó REST API ẹ̀rọ náà (dash-node) — iṣẹ́ Node kékeré kan lórí 127.0.0.1:4000, tí nginx ń ṣe àgbékalẹ̀ tí pdsm sì ń bójú tó. Ó ń darí gbogbo iṣẹ́ ilé-ìkàwé nínú ìṣàmúlò: ṣíṣàwárí àti gbígba àwọn Ìwé sílẹ̀, fifi kún àti pípa àkóónú Wikipedia àti ZIM rẹ́, àwọn ìpàdé Kolibri, Máàpù àti ipò ètò. Yàtọ̀ sí àwọn módùlù àkóónú, o kò lè fi í sórí ẹ̀rọ tàbí yọ ọ́ kúrò. Ó ń wá pẹ̀lú ìṣàmúlò náà ó sì máa ń wà nígbà gbogbo. Nígbà tí ẹ̀dà tuntun bá wà, o lè tún un kọ́ ní ibẹ̀.
@@ -1258,6 +1262,7 @@
Ìmúpadàbọ̀ kan ń lọ lọ́wọ́. Jọ̀wọ́ dúró kí ó parí.
Fifi-sórí-ẹ̀rọ kan ń lọ lọ́wọ́. Jọ̀wọ́ dúró kí ó parí.
Ìgbàsílẹ̀ kan ń lọ lọ́wọ́. Jọ̀wọ́ dúró kí ó parí.
+ Ìmúdójúìwọ̀n dáṣíbọ̀ọ̀dù ń lọ lọ́wọ́. Jọ̀wọ́ dúró kí ó parí.
Àwọn ìgbàsílẹ̀ tí ń dúró
Kò sí ìgbàsílẹ̀ tí ń dúró.
Ohun kan ń gbà sílẹ̀ báyìí
diff --git a/controller/app/src/main/res/values-zh-rCN/strings.xml b/controller/app/src/main/res/values-zh-rCN/strings.xml
index e8f33ce0d..d04bef6e2 100644
--- a/controller/app/src/main/res/values-zh-rCN/strings.xml
+++ b/controller/app/src/main/res/values-zh-rCN/strings.xml
@@ -1116,14 +1116,18 @@
REST 核心 · 已安装 v%1$s
重建
重建 REST 核心?
- 将 dash-node 更新到最新版本。重建期间服务器会短暂不可用;完成前请勿离开此界面。
+ 将 dash-node 更新到最新版本。它在后台运行;重建期间服务器会短暂不可用。
重建然后你选择方式:下载,或从另一台手机复制。
正在更新仪表板
正在原地更新… 仪表板会短暂重启。
仪表板已更新。
更新失败 — 已保留先前版本。
- 更新用时比平常更长。它会在后台完成 — 稍后查看版本。
无法开始更新。请重试。
+ 正在后台更新仪表板。
+ 仪表板更新
+ 取消
+ 更新即将完成,现在无法取消。
+ 无法取消更新。它将继续运行。
Dashboard (REST API)
系统核心 · 始终已安装
仪表板是设备的 REST API 核心(dash-node)——运行在 127.0.0.1:4000 上的一个小型 Node 服务,由 nginx 提供前端、pdsm 监管。它驱动应用内的每一项图书馆操作:浏览和下载图书、添加和删除 Wikipedia 与 ZIM 内容、Kolibri 会话、地图和系统状态。与内容模块不同,你无法安装或移除它。它随应用附带并始终存在。当有更新的构建时,你可以就地重新构建。
@@ -1239,6 +1243,7 @@
恢复正在进行中。请等待其完成。
安装正在进行中。请等待其完成。
下载正在进行中。请等待其完成。
+ 仪表板更新正在进行。请等待其完成。
待处理的下载
没有待处理的下载。
正在下载
diff --git a/controller/app/src/main/res/values/strings.xml b/controller/app/src/main/res/values/strings.xml
index 80435d30e..c357b85d8 100644
--- a/controller/app/src/main/res/values/strings.xml
+++ b/controller/app/src/main/res/values/strings.xml
@@ -1190,6 +1190,7 @@
A restore is in progress. Please wait for it to finish.
An install is in progress. Please wait for it to finish.
A download is in progress. Please wait for it to finish.
+ A dashboard update is in progress. Please wait for it to finish.
Still setting up the system. You can leave the app while setup runs in the background.
@@ -1343,15 +1344,23 @@
REST core · installed v%1$s
Rebuild
Rebuild the REST core?
- Updates dash-node to the latest version. The server is briefly unavailable while it rebuilds; don\'t leave this screen until it finishes.
+ Updates dash-node to the latest version. It runs in the background; the server is briefly unavailable while it rebuilds.
Rebuild needs an internet connection.
-
+
Updating the dashboard
Updating in place… the dashboard restarts briefly.
Dashboard updated.
Update failed — the previous version was kept.
- The update is taking longer than usual. It will finish in the background — check the version in a moment.
Couldn\'t start the update. Try again.
+
+ Updating the dashboard in the background.
+ Dashboard update
+
+ Cancel
+ The update is finishing and can\'t be canceled now.
+ Couldn\'t cancel the update. It will keep running.
Dashboard (REST API)
System core · always installed
diff --git a/static/dashboard/CHANGELOG.md b/static/dashboard/CHANGELOG.md
index 999bdfcd1..fab665bc5 100644
--- a/static/dashboard/CHANGELOG.md
+++ b/static/dashboard/CHANGELOG.md
@@ -4,6 +4,7 @@ One line per version, newest first. Every REST-facing change bumps the version i
(the app surfaces it via `/system/dashboard/update-check` and the "Update available" pill), so this
file is the human record of what each bump enables. Keep entries short: `version - change (TICKET)`.
+- **1.2.9** - Cancelable dashboard self-update (ADFA-5333). New `POST /system/dashboard/rebuild/cancel`: stops an in-flight rebuild cleanly while it is still **building** (git fetch + staging build + smoke test — none of which touch the live dashboard) by signaling the detached `setsid` session group; **refused during "promoting"** (the short dist-swap + restart window) so the swap is never interrupted mid-flight, and a no-op (409) when nothing is running. To support this, `tools/rebuild-dashboard.sh` now records its coarse phase (`building`/`promoting`) and its session-leader pid, and its cleanup trap fires on TERM/INT so a canceled run leaves no staging behind. Pairs with the app running the update in the background with a Cancel action (ADFA-5333). (ADFA-5333)
- **1.2.7** - Books homologated to the ZIM contract (ADFA-4893). The books runner now surfaces its reconnect state on the poll (`retryAttempt`/`retryTotal` via `ctx.reportRetry`), so the app can show "Reconnecting… n of N" like ZIM/rootfs (books' per-item budget is 6 tries → shows n of 5, same label). And the runner is now **idempotent on resume**: it skips books already in the Calibre-Web library (matched by title) instead of re-downloading + re-uploading them — which used to duplicate entries and reset the percent to 0 — so a resume, or a re-run after process death, is safe. (ADFA-4893)
- **1.2.6** - Visible reconnect + honest resume for kiwix (ADFA-4893). The outer reconnect loop now surfaces its state on the poll — `GET /kiwix/jobs/:id` gains `retryAttempt`/`retryTotal` (in-memory, no DB change) — so the app can show "Reconnecting n/5". The schedule is now an explicit **3/6/9/18/36 s** (5 visible waits) instead of exponential, and **pause breaks a backoff wait immediately** (the loop takes `ctx.signal`), so the on-screen X during a reconnect pauses at once. Resume no longer flashes 0%: `runKiwix` stopped sending `percent: 0` on its initial update, so a resumed job keeps its retained percent until aria2 `--continue` re-reports (a fresh job stays `-1` until the first progress). (ADFA-4893)
- **1.2.5** - maps stop/cancel actually stop (ADFA-4896, server half). The maps runner shelled through `sudo`, so pause/cancel only killed the wrapper — the `tile-extract.py` worker kept running, finished in the background, and the region ended up listed even after cancel. Fixes: the extract now spawns **detached** and the engine kills the whole **process group** (`-pid`) so the worker dies with it; the engine also drops **late updates** once a job is paused/canceled, so a slow-dying line can't resurrect the phase; and the maps runner now honors **pause vs cancel** (`classifyStop`) and **purges the partial** on any non-success — it prunes the half-written pmtiles the script leaves in `/library/downloads/maps` (which `tile-extract.py delete` does NOT touch) and runs `delete` (which refreshes extracts.json) for the rare race where the extract finished just as the stop landed. Pause is stop+restart (resume re-extracts clean), cancel removes the region entirely. `cancel()` now also finalizes a **paused** job to `canceled` (Cancel-after-Stop) instead of leaving it stuck at `paused`. No maps per-layer checkpoint yet (→ ADFA-4898). Device-verified: group-kill reaches the pmtiles worker under proot (no background survivor / phantom region). (ADFA-4896)
diff --git a/static/dashboard/package.json b/static/dashboard/package.json
index fee58a417..10e8b23e9 100644
--- a/static/dashboard/package.json
+++ b/static/dashboard/package.json
@@ -1,6 +1,6 @@
{
"name": "dashboard-console",
- "version": "1.2.8",
+ "version": "1.2.9",
"description": "",
"main": "index.js",
"scripts": {
diff --git a/static/dashboard/routes.ts b/static/dashboard/routes.ts
index 16d2ad9db..43f9412d2 100644
--- a/static/dashboard/routes.ts
+++ b/static/dashboard/routes.ts
@@ -244,6 +244,11 @@ apiRouter.post('/kiwix/delete', (req: Request, res: Response): void => {
// restarting dash-node mid-run never kills it. Declared before the generic /:type/* routes.
const REBUILD_SCRIPT = '/opt/iiab-android/tools/rebuild-dashboard.sh';
const REBUILD_STATUS_FILE = '/var/run/dash-rebuild.status';
+// ADFA-5333: cancel support. The script writes its phase (building | promoting) and its session-leader
+// pid; cancel is safe (and allowed) only while building — that window never touches the live dashboard.
+const REBUILD_PHASE_FILE = '/var/run/dash-rebuild.phase';
+const REBUILD_PID_FILE = '/var/run/dash-rebuild.pid';
+const REBUILD_LOCK_DIR = '/var/run/dash-rebuild.lock';
// ADFA-5051: the remote branch that update-check compares against AND the rebuild pulls. Defaults to
// mainline; set K2GO_DASH_BRANCH in the dash-node env to point a test box at a feature branch (e.g.
// to exercise the live self-update before merging to main) without touching code. Keep it unset in
@@ -292,6 +297,57 @@ apiRouter.post('/system/dashboard/rebuild', (_req: Request, res: Response): void
}
});
+// ADFA-5333: cancel an in-flight rebuild cleanly. Safe only while "building" (steps 1-3: git/build/smoke,
+// which never touch the live dashboard) — we signal the detached session group (script + yarn/node) to
+// stop, its EXIT/TERM trap purges staging, and we reset the state we own so the live version is left
+// exactly as it was. Refused once "promoting" (the short dist-swap + restart window) so the swap is never
+// interrupted mid-flight, and a no-op when nothing is running.
+apiRouter.post('/system/dashboard/rebuild/cancel', (_req: Request, res: Response): void => {
+ let state = 'idle';
+ try { state = fs.readFileSync(REBUILD_STATUS_FILE, 'utf8').trim() || 'idle'; } catch { /* none */ }
+ if (state !== 'running') { res.status(409).json({ error: 'no rebuild running', cancelled: false }); return; }
+ let phase = 'building';
+ try { phase = fs.readFileSync(REBUILD_PHASE_FILE, 'utf8').trim() || 'building'; } catch { /* default building */ }
+ if (phase === 'promoting') { res.status(409).json({ error: 'promoting', promoting: true, cancelled: false }); return; }
+ let pid = 0;
+ try { pid = parseInt(fs.readFileSync(REBUILD_PID_FILE, 'utf8').trim(), 10); } catch { /* no pid yet */ }
+ if (!Number.isFinite(pid) || pid <= 1) {
+ // The run has just started (status is 'running') but hasn't recorded its session pid yet, so we
+ // can't signal it. Do NOT claim success or touch the status — the app can retry in a moment.
+ res.status(409).json({ error: 'cancel not ready', cancelled: false }); return;
+ }
+ // Verify the pid is actually OUR rebuild script before signaling — never SIGTERM a recycled/unrelated
+ // pid. /proc//cmdline is NUL-separated; a substring match on the script name is enough.
+ let cmdline = '';
+ try { cmdline = fs.readFileSync(`/proc/${pid}/cmdline`, 'utf8'); } catch { /* process gone / no proc */ }
+ const healStale = (): void => {
+ try { fs.writeFileSync(REBUILD_STATUS_FILE, 'idle'); } catch { /* best effort */ }
+ try { fs.rmdirSync(REBUILD_LOCK_DIR); } catch { /* maybe already gone */ }
+ try { fs.unlinkSync(REBUILD_PHASE_FILE); } catch { /* maybe already gone */ }
+ try { fs.unlinkSync(REBUILD_PID_FILE); } catch { /* maybe already gone */ }
+ };
+ if (cmdline === '' && !fs.existsSync(`/proc/${pid}`)) {
+ // The run already exited but left stale state (status 'running' with a dead pid) — heal it and
+ // report success; there is nothing left to stop.
+ healStale();
+ res.status(200).json({ ok: true, cancelled: true }); return;
+ }
+ if (!cmdline.includes('rebuild-dashboard.sh')) {
+ // The pid is alive but was recycled by an unrelated process — refuse to signal it.
+ res.status(409).json({ error: 'stale pid', cancelled: false }); return;
+ }
+ try {
+ // Signal the whole detached session group; the script's on_cancel/EXIT trap sets status idle and
+ // purges staging + lock/phase/pid. We also write idle + best-effort rmdir here as belt-and-suspenders.
+ try { process.kill(-pid, 'SIGTERM'); } catch { /* group already gone */ }
+ try { process.kill(pid, 'SIGTERM'); } catch { /* leader already gone */ }
+ healStale();
+ res.status(200).json({ ok: true, cancelled: true });
+ } catch (e: any) {
+ res.status(500).json({ error: e?.message || 'cancel failed', cancelled: false });
+ }
+});
+
// ADFA-5026: live "is a newer dash-node available?" check. Unlike /system/version (which reads the
// installed package.json off disk, offline), this compares the installed version against the mainline
// remote: git fetch + read origin/main's static/dashboard/package.json. Everything stays in the rootfs
diff --git a/tools/rebuild-dashboard.sh b/tools/rebuild-dashboard.sh
index 40194a083..dbc447acf 100755
--- a/tools/rebuild-dashboard.sh
+++ b/tools/rebuild-dashboard.sh
@@ -41,11 +41,18 @@ SMOKE="${K2GO_SMOKE:-$CLONE_DIR/tools/dashboard-smoketest.sh}"
STATUS="/var/run/dash-rebuild.status"
LOG="/var/log/dash-rebuild.log"
LOCK="/var/run/dash-rebuild.lock"
+# ADFA-5333: coarse phase for a safe cancel. "building" (steps 1-3) never touches the live dashboard, so
+# a cancel there just kills this run and purges staging; "promoting" (step 4: dist swap + restart) is the
+# short, non-cancelable window — the cancel endpoint refuses while it is set. PID lets the endpoint signal
+# this whole detached session (setsid group) to stop.
+PHASE="/var/run/dash-rebuild.phase"
+PIDFILE="/var/run/dash-rebuild.pid"
# ADFA-4893: also echo to the console so a manual foreground run shows progress live (not just the
# log file). Harmless when launched detached (POST .../rebuild) — stdout goes nowhere then.
log() { _m="[$(date '+%Y-%m-%d %H:%M:%S')] $*"; echo "$_m" >> "$LOG" 2>/dev/null; echo "$_m"; }
set_status() { echo "$1" > "$STATUS" 2>/dev/null || true; }
+set_phase() { echo "$1" > "$PHASE" 2>/dev/null || true; }
# ADFA-5051: proot renders some native-build symlinks (e.g. in better-sqlite3/build) as ".l2s." loops
# that `rm -rf` can't recurse into (ELOOP -> "Directory not empty"), but a direct unlink removes them.
# Sweep them first, then remove the tree — so staging is always cleanly removable.
@@ -63,14 +70,25 @@ verify_live() {
done
return 1
}
-cleanup() { [ -n "${TESTPID:-}" ] && kill "$TESTPID" 2>/dev/null || true; purge_staging; rmdir "$LOCK" 2>/dev/null || true; }
+cleanup() { [ -n "${TESTPID:-}" ] && kill "$TESTPID" 2>/dev/null || true; purge_staging; rmdir "$LOCK" 2>/dev/null || true; rm -f "$PHASE" "$PIDFILE" 2>/dev/null || true; }
fail() { log "FAIL: $*"; set_status "error"; cleanup; exit 1; }
+# ADFA-5333: a cancel (SIGTERM/INT during the building phase) self-heals the state — set the status to
+# idle BEFORE cleanup so a killed build can never leave a wedged "running" (the endpoint also writes idle,
+# but this makes it true regardless of whether the endpoint completes). Ignored during promote (trapped off).
+on_cancel() { log "canceled"; set_status "idle"; cleanup; exit 0; }
# Single-flight: mkdir is atomic.
mkdir "$LOCK" 2>/dev/null || { echo "another rebuild is running" >&2; exit 3; }
+# ADFA-5333: clean staging + phase/pid on ANY exit, including a cancel signal (TERM/INT). Killing this
+# session during steps 1-3 therefore leaves nothing behind and the live dashboard untouched.
trap cleanup EXIT
+trap on_cancel INT TERM
: > "$LOG" 2>/dev/null || true
set_status "running"
+set_phase "building"
+# ADFA-5333: record THIS shell's pid. Under setsid it is the session/group leader, so the cancel endpoint
+# can signal the whole group (this script + yarn/node/tar children) with kill(-pid).
+echo $$ > "$PIDFILE" 2>/dev/null || true
log "rebuild start (branch=$BRANCH, clone=$CLONE_DIR)"
[ -d "$SRC" ] || fail "source $SRC not found"
@@ -112,6 +130,11 @@ kill "$TESTPID" 2>/dev/null || true; wait "$TESTPID" 2>/dev/null || true; TESTPI
# 4) promote — swap the dist FIRST and verify it live; only advance source + package.json (and the
# nginx vhost) AFTER the live check passes. That way a failed verify rolls back the dist and the
# version/source were NEVER touched, so the box can't report a version it isn't actually running.
+# ADFA-5333: entering the non-cancelable window (dist swap + restart). The cancel endpoint refuses once
+# the phase is "promoting"; we also IGNORE stop signals through the swap so a cancel that raced the phase
+# read can't land mid-swap. (EXIT still runs cleanup on normal completion.) Restored after step 5.
+set_phase "promoting"
+trap '' INT TERM
log "staged build passed — promoting"
rm -rf "$BACKUP"
[ -d "$LIVE/dist" ] && cp -a "$LIVE/dist" "$BACKUP"
@@ -156,4 +179,7 @@ else
rm -rf "$BACKUP"
set_status "error"
fi
+# Promote done: the terminal status (done/error) is already written and only cleanup remains, so keep
+# INT/TERM ignored — a late signal must NOT run on_cancel and overwrite that status with "idle". The EXIT
+# trap still runs cleanup on the way out.
cleanup