Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ public final class DeepOpService extends Service {

/** K2GO-372: a restore is one run of three passes — stage the file, verify it, extract it — so they
* share one bar instead of each filling it and sending it back to zero. */
private static final int RESTORE_PASSES = 3;
private static final int COPY_PASS = 0;
// K2GO-448: public so the restore UI (BackupJobFragment) labels its optimistic first-stage status
// from the same source, instead of hardcoding the count. The owner of the pass model is this service.
public static final int RESTORE_PASSES = 3;
public static final int COPY_PASS = 0;
private static final int VERIFY_PASS = 1;
private static final int EXTRACT_PASS = 2;

Expand Down Expand Up @@ -186,7 +188,12 @@ public int onStartCommand(Intent intent, int flags, int startId) {
// Taking the lock here also sends the box down (RESTORE is a STOPPED-class holder, so desired
// goes DOWN on the next reconciler tick) — which is what the confirm the user just answered
// promised, and it means the explicit stop below has little left to wait for.
stepText = getString(restoring ? R.string.k2go_br_status_copying : R.string.k2go_br_status_stopping);
// K2GO-448: the restore's first stage ("[1/3] Copying the file") must carry the prefix from the
// start, so the notification and the initial indeterminate state match the progress emits below.
stepText = restoring
? org.appdevforall.k2go.deploy.domain.ExtractProgress.stepped(
COPY_PASS, RESTORE_PASSES, getString(R.string.k2go_br_status_copying))
: getString(R.string.k2go_br_status_stopping);
startForeground(NOTIFICATION_ID, buildNotification(stepText));
acquireHardwareLocks();

Expand Down Expand Up @@ -388,7 +395,8 @@ private String stageArchive(Uri src, File temp, long size) {
.perSecond(copied, now - startMs);
final long eta = org.appdevforall.k2go.deploy.domain.ExtractProgress
.etaSeconds(copied, size, rate);
main.post(() -> setStep(getString(R.string.k2go_br_status_copying), pct, eta));
main.post(() -> setStep(org.appdevforall.k2go.deploy.domain.ExtractProgress
.stepped(COPY_PASS, RESTORE_PASSES, getString(R.string.k2go_br_status_copying)), pct, eta));
}
}
out.flush();
Expand Down Expand Up @@ -435,7 +443,8 @@ private void attemptVerifyAndExtract(final String path) {
if (done || passRunning) return; // K2GO-384: one verify+extract pass at a time (service is the owner)
passRunning = true;
currentCancelKind = DeepOpState.CancelKind.CANCELLABLE; // K2GO-384: verify -- cancellable (kill + hold)
setStep(getString(R.string.k2go_br_status_checking), 0);
setStep(org.appdevforall.k2go.deploy.domain.ExtractProgress
.stepped(VERIFY_PASS, RESTORE_PASSES, getString(R.string.k2go_br_status_checking)), 0);
final File destParent = new File(getFilesDir(), "rootfs");
new TarExtractor().startExtraction(this, path, destParent.getAbsolutePath(), true,
cancelBeforeExtract, pauseRequested, forceExtractCancel,
Expand Down Expand Up @@ -500,9 +509,11 @@ public void onExtractPhase(TarExtractor.Phase phase, int passPercent,
final int unified = org.appdevforall.k2go.deploy.domain.ExtractProgress
.unifiedPercent(passPercent, extracting ? EXTRACT_PASS : VERIFY_PASS,
RESTORE_PASSES);
final String label = getString(extracting
? R.string.k2go_br_status_restoring
: R.string.k2go_br_status_checking);
final String label = org.appdevforall.k2go.deploy.domain.ExtractProgress.stepped(
extracting ? EXTRACT_PASS : VERIFY_PASS, RESTORE_PASSES,
getString(extracting
? R.string.k2go_br_status_restoring
: R.string.k2go_br_status_checking));
// K2GO-384: pass through the per-pass ETA TarExtractor already computed (was dropped).
main.post(() -> setStep(label, unified, etaSeconds));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,27 @@ public static int unifiedPercent(int passPercent, int passIndex, int passCount)
return v > 99 ? 99 : v;
}

/**
* K2GO-448: a stage prefix for a multi-pass progress label, so the single unified bar reads as
* what it is: "[1/3] Copying the file", "[2/3] Checking the file", "[3/3] Restoring". The unified
* bar is monotone, but each pass reports its own ETA, so without the position the resetting time
* reads as the whole run slowing down. passIndex is 0-based (like unifiedPercent); the shown
* position is passIndex + 1, clamped the same way so a bad index never prints out of range. A
* single-pass run has nothing to disambiguate, so callers that do not want a prefix (a one-stage
* backup) simply do not call this. The "[N/M]" form carries no words, so it needs no translation.
*
* @param passIndex this pass, 0-based.
* @param passCount total passes in the run.
* @param label the stage's own (already localized) label.
* @return "[N/M] " + label.
*/
public static String stepped(int passIndex, int passCount, String label) {
if (passCount < 1) passCount = 1;
if (passIndex < 0) passIndex = 0;
if (passIndex > passCount - 1) passIndex = passCount - 1;
return "[" + (passIndex + 1) + "/" + passCount + "] " + (label == null ? "" : label);
}

/** First line of a possibly multi-line label, trimmed. Null-safe. */
public static String firstLine(String s) {
if (s == null) return "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,11 @@ private void prepareRestore(Uri uri) {
.setMessage(getString(R.string.k2go_br_restore_warn))
.setPositive(R.string.k2go_br_restore_confirm, BrandDialog.Role.DESTRUCTIVE, () -> {
beginRunning();
setStatusAnimated(getString(R.string.k2go_br_status_copying));
// K2GO-448: same "[1/3]" prefix the service posts, from the same source, so the
// optimistic status matches instead of flashing an unprefixed "Copying the file".
setStatusAnimated(org.appdevforall.k2go.deploy.domain.ExtractProgress.stepped(
DeepOpService.COPY_PASS, DeepOpService.RESTORE_PASSES,
getString(R.string.k2go_br_status_copying)));
DeepOpService.startRestore(requireContext(), uri);
})
.setNegative(R.string.cancel, () -> popToIntro(false))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ public class LibraryActivity extends AppCompatActivity
private static final long GATE_SAFETY_MS = 25000L;
/** Nothing installed → nothing to boot: dismiss the gate promptly instead of waiting. */
private static final long NO_SYSTEM_GATE_MS = 900L;
// K2GO-448: the rootfs install shows two measured bars (download, then verify+extract); one source
// for the count so both stage labels below agree. PROVISIONING is an unnumbered spinner, not a bar.
private static final int ROOTFS_INSTALL_STAGES = 2;
/** Set by the Setup "Download" so the gate waits for the install to finish, not a timeout. */
public static final String EXTRA_INSTALLING = "installing";
// K2GO-391: the disk guard's notification opens this activity with a pre-filled report to send.
Expand Down Expand Up @@ -491,8 +494,11 @@ private void showInstallProgress(InstallState st) {
// from the IPv4/IPv6 probe, so without it the same three probes scroll past a second and
// a third time with nothing to say which time this is — and the count is precisely what
// tells the user whether waiting is still worth it.
installStatus.setText(st.message.isEmpty()
? getString(R.string.k2go_downloading_library) : st.message);
// K2GO-448: the rootfs install is two measured bars (download, then verify+extract);
// mark this one as stage 1 of 2 so the per-stage time does not read as a slowdown.
installStatus.setText(org.appdevforall.k2go.deploy.domain.ExtractProgress.stepped(0, ROOTFS_INSTALL_STAGES,
st.message.isEmpty()
? getString(R.string.k2go_downloading_library) : st.message));
installBar.setIndeterminate(false);
installBar.setProgress(st.percent);
// ADFA-4895: one table per line, sized to the line, rather than one table stretched
Expand Down Expand Up @@ -528,8 +534,10 @@ private void showInstallProgress(InstallState st) {
// bar + % + ETA + current file — so there is no "first nothing, then detail" asymmetry.
// Only the verb on the status line changes at the handoff.
boolean verifying = st.phase == InstallState.Phase.VERIFYING;
installStatus.setText(org.appdevforall.k2go.deploy.domain.ExtractProgress.firstLine(
getString(verifying ? R.string.k2go_verifying_files : R.string.install_status_extracting)));
// K2GO-448: stage 2 of 2 (verify and extract share one unified bar; only the verb changes).
installStatus.setText(org.appdevforall.k2go.deploy.domain.ExtractProgress.stepped(1, ROOTFS_INSTALL_STAGES,
org.appdevforall.k2go.deploy.domain.ExtractProgress.firstLine(
getString(verifying ? R.string.k2go_verifying_files : R.string.install_status_extracting))));
if (st.percent < 0) {
// Indeterminate fallback: before the first byte lands, or an archive whose size we
// couldn't read (no byte-based %). Animated hint on the DETAIL line (where the % goes).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,27 @@ public void anythingUnrecognisedIsStillShown() {
assertEquals(ExtractProgress.unifiedPercent(0, 0, 3), ExtractProgress.unifiedPercent(0, -1, 3));
assertEquals(50, ExtractProgress.unifiedPercent(50, 0, 0)); // a run of no passes is one pass
}

// ---- K2GO-448: the stage prefix for the unified multi-pass bar ----

@Test public void steppedPrefixesTheRestoreStagesInOrder() {
assertEquals("[1/3] Copying the file", ExtractProgress.stepped(0, 3, "Copying the file"));
assertEquals("[2/3] Checking the file", ExtractProgress.stepped(1, 3, "Checking the file"));
assertEquals("[3/3] Restoring", ExtractProgress.stepped(2, 3, "Restoring"));
}

@Test public void steppedPrefixesTheTwoRootfsStages() {
assertEquals("[1/2] Downloading", ExtractProgress.stepped(0, 2, "Downloading"));
assertEquals("[2/2] Extracting", ExtractProgress.stepped(1, 2, "Extracting"));
}

@Test public void steppedClampsOutOfRangeLikeUnifiedPercent() {
assertEquals("[3/3] x", ExtractProgress.stepped(9, 3, "x")); // past the end -> last pass
assertEquals("[1/3] x", ExtractProgress.stepped(-1, 3, "x")); // before the start -> first pass
assertEquals("[1/1] x", ExtractProgress.stepped(0, 0, "x")); // a run of no passes is one pass
}

@Test public void steppedNullLabelIsEmptyNotNullText() {
assertEquals("[1/3] ", ExtractProgress.stepped(0, 3, null));
}
}