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
30 changes: 24 additions & 6 deletions app/src/main/java/com/httrack/android/HTTrackActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ protected static class VERSION_CODES {
// "Project name" pane is dirty
protected boolean dirtyNamePane;

// Project whose saved profile is loaded in the map (reload-guard sentinel); null if none.
// Distinct from the map name key, which plain Back navigation writes before any load.
protected String loadedProjectName;

// Handler to execute code in UI thread
private final Handler handlerUI = new Handler();

Expand Down Expand Up @@ -699,6 +703,7 @@ protected void onCreate(final Bundle savedInstanceState) {
// Clear map (useful to get dynamic fields)
mapper.setContext(this);
mapper.resetMap();
loadedProjectName = null;

// Go to first pane now
setPane(0);
Expand Down Expand Up @@ -2275,9 +2280,15 @@ public void run() {
});
}

/** Whether the setup pane should (re)load the selected project's saved profile; see loadedProjectName. */
static boolean shouldReloadProfile(final String selectedName,
final String loadedName, final boolean dirty) {
return dirty || loadedName == null || !loadedName.equals(selectedName);
}

/**
* Validate the current pane
*
*
* @return true if the current pane is valid
*/
protected boolean validatePane() {
Expand All @@ -2298,11 +2309,9 @@ protected boolean validatePane() {
// Check project name
final String name = getFieldText(R.id.fieldProjectName);
if (OptionsMapper.isStringNonEmpty(name)) {
// Changed name ?
final String prevName = getMap(R.id.fieldProjectName);
if (prevName == null || !prevName.equals(name) || dirtyNamePane) {
// We need to put immediately the name in the map to be able to
// unserialize.
// Reload when the selection differs from the loaded profile (see shouldReloadProfile).
if (shouldReloadProfile(name, loadedProjectName, dirtyNamePane)) {
// Put the name in the map first so unserialize() can resolve the profile.
try {
mapper.resetMap();
setMap(R.id.fieldProjectName, name);
Expand All @@ -2311,6 +2320,7 @@ protected boolean validatePane() {
// Ignore (if not found)
} finally {
dirtyNamePane = false;
loadedProjectName = name;
}
}
// A crafted winprofile.ini can overwrite the name with a path that escapes the root.
Expand Down Expand Up @@ -2950,6 +2960,9 @@ protected void saveInstanceState(final Bundle outState) {
// Map keys
outState.putParcelable("com.httrack.android.map", mapper.serialize());

// Which project's profile the map holds, so the reload guard survives recreation.
outState.putString("com.httrack.android.loadedProjectName", loadedProjectName);

// Current pane
outState.putInt("com.httrack.android.pane_id", pane_id);

Expand Down Expand Up @@ -3090,6 +3103,11 @@ protected void restoreInstanceState(final Bundle savedInstanceState) {
// Load map settings
loadParcelable(data);

// Restore the reload-guard sentinel alongside the map it tracks; else a post-restore
// Back/Next would treat the live map as stale and wipe the user's edits.
loadedProjectName = savedInstanceState
.getString("com.httrack.android.loadedProjectName");

// The progress pane means a live crawl; after process death none exists, so land on the
// setup pane instead, where isInterruptedProfile() defaults the action to Continue.
final int paneId = (id == LAYOUT_MIRROR_PROGRESS && !hasLiveRunner())
Expand Down
37 changes: 37 additions & 0 deletions app/src/test/java/com/httrack/android/ProjectReloadTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.httrack.android;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.junit.Test;

/**
* Guards HTTrackActivity.shouldReloadProfile, the decision that loads a selected project's
* saved URL/options. Regression: Select project -> Back -> Next -> Next left the URL blank
* because the guard trusted the map name key, which Back had already written.
*/
public class ProjectReloadTest {
@Test
public void reloadsWhenNothingLoadedYet() {
// Fresh selection, no profile loaded: must load, even though the map name key already
// holds the name (written by Back navigation). This is the reported bug.
assertTrue(HTTrackActivity.shouldReloadProfile("Foo", null, false));
}

@Test
public void reloadsWhenSelectionChanged() {
assertTrue(HTTrackActivity.shouldReloadProfile("Bar", "Foo", false));
}

@Test
public void reloadsWhenDirtyAfterRestore() {
assertTrue(HTTrackActivity.shouldReloadProfile("Foo", "Foo", true));
}

@Test
public void skipsWhenSameProfileAlreadyLoaded() {
// The one case that must NOT reload: reloading here would wipe URL/options the user
// typed on the setup pane then navigated back past.
assertFalse(HTTrackActivity.shouldReloadProfile("Foo", "Foo", false));
}
}
Loading