Skip to content

Commit bfcfc66

Browse files
ADFA-4900: wizard maps drive a per-layer install via local_vars + runrole
Wire the wizard maps Choose selection through to a real install using the existing module-queue engine instead of the mock. MapsChooseFragment now carries the machine level keys (base/satellite/terrain/search; null = off) through Confirm to Preparing; Preparing starts the install (a module queue of {maps} plus the selection) and follows the real ModuleQueueRepository state. InstallService, for the maps module, writes the full maps_* var set to local_vars from the selection (vector quality; satellite/terrain zoom or none; search engine + static db; region downloader; ne6; preset FQRs) via sed-delete + echo (append-if-missing), validated against an allowlist, then runs runrole maps under the shared success/failure verdict + revert-on-fail + progress. Mapping confirmed against roles/maps tasks/install_frontend.yml.
1 parent 47d438b commit bfcfc66

6 files changed

Lines changed: 141 additions & 52 deletions

File tree

‎controller/app/src/main/java/org/iiab/controller/install/presentation/InstallService.java‎

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,15 @@ public final class InstallService extends Service {
8787
public static final String MODE_INSTALL = "install";
8888
public static final String MODE_RESET = "reset";
8989

90+
// ADFA-4900: per-layer maps config carried with a module queue of {"maps"}. When present, the
91+
// "maps" module writes the full maps_* var set to local_vars (from the wizard selection) before
92+
// runrole, instead of the generic <key>_install/_enabled echo. Values are validated against a
93+
// fixed allowlist before interpolation (D2).
94+
public static final String EXTRA_MAPS_VECTOR = "mapsVector"; // nat-z8 | osm-z11 | osm-z14
95+
public static final String EXTRA_MAPS_SAT = "mapsSat"; // 7|9|11|13 | none
96+
public static final String EXTRA_MAPS_TERRAIN = "mapsTerrain"; // 7|8|9|10 | none
97+
public static final String EXTRA_MAPS_SEARCH = "mapsSearch"; // boolean: static pop-1k-cities on/off
98+
9099
private PowerManager.WakeLock wakeLock;
91100
private WifiManager.WifiLock wifiLock;
92101

@@ -113,6 +122,17 @@ public final class InstallService extends Service {
113122
private java.util.Deque<String> moduleQueue;
114123
private java.util.List<String> failedModules;
115124

125+
// ADFA-4900: wizard maps per-layer config (only set when the queue is {"maps"} from the wizard).
126+
private boolean hasMapsConfig;
127+
private String mapsVector, mapsSat, mapsTerrain;
128+
private boolean mapsSearchOn;
129+
private static final java.util.Set<String> MAPS_VECTOR_OK =
130+
new java.util.HashSet<>(java.util.Arrays.asList("nat-z8", "osm-z11", "osm-z14"));
131+
private static final java.util.Set<String> MAPS_SAT_OK =
132+
new java.util.HashSet<>(java.util.Arrays.asList("none", "7", "9", "11", "13"));
133+
private static final java.util.Set<String> MAPS_TERRAIN_OK =
134+
new java.util.HashSet<>(java.util.Arrays.asList("none", "7", "8", "9", "10"));
135+
116136
private File iiabRootDir; // filesDir/rootfs
117137
private File debianRootfs; // filesDir/rootfs/installed-rootfs/iiab
118138

@@ -156,6 +176,13 @@ public int onStartCommand(Intent intent, int flags, int startId) {
156176
}
157177
failedModules = new java.util.ArrayList<>();
158178

179+
// ADFA-4900: pick up the wizard maps per-layer selection, if any.
180+
mapsVector = intent.getStringExtra(EXTRA_MAPS_VECTOR);
181+
mapsSat = intent.getStringExtra(EXTRA_MAPS_SAT);
182+
mapsTerrain = intent.getStringExtra(EXTRA_MAPS_TERRAIN);
183+
mapsSearchOn = intent.getBooleanExtra(EXTRA_MAPS_SEARCH, false);
184+
hasMapsConfig = mapsVector != null && moduleQueue.contains("maps");
185+
159186
startForeground(NOTIFICATION_ID, buildNotification(getString(R.string.install_busy_modules)));
160187
acquireHardwareLocks();
161188
persistQueue();
@@ -658,7 +685,11 @@ private void installNextModule() {
658685
return;
659686
}
660687

661-
String installCmd = "sed -i -E '/^[[:space:]]*" + nextModule + "_(install|enabled)[[:space:]]*:/d' /etc/iiab/local_vars.yml && " +
688+
// ADFA-4900: for the wizard maps flow, write the full per-layer maps_* var set before
689+
// runrole (the generic <key>_install/_enabled echo can't express quality/off/search).
690+
final String installCmd = ("maps".equals(nextModule) && hasMapsConfig)
691+
? mapsInstallCmd()
692+
: "sed -i -E '/^[[:space:]]*" + nextModule + "_(install|enabled)[[:space:]]*:/d' /etc/iiab/local_vars.yml && " +
662693
"echo '" + nextModule + "_install: True' >> /etc/iiab/local_vars.yml && " +
663694
"echo '" + nextModule + "_enabled: True' >> /etc/iiab/local_vars.yml && " +
664695
"cd /opt/iiab/iiab && ./runrole " + roleName;
@@ -717,6 +748,37 @@ private void revertModuleInLocalVars(String module, Runnable then) {
717748
});
718749
}
719750

751+
/**
752+
* ADFA-4900: build the maps runrole command from the wizard's per-layer selection. Translates
753+
* the selection into the maps role's local_vars (roles/maps/tasks/install_frontend.yml):
754+
* satellite/terrain "none" turns the layer off; search maps to maps_search_engine +
755+
* maps_search_static_db. Every var the role's iiab.ini step references is written so the play
756+
* never hits an undefined var. Values are validated against a fixed allowlist (D2); anything
757+
* unexpected falls back to a safe default. Uses sed-delete + echo (append-if-missing).
758+
*/
759+
private String mapsInstallCmd() {
760+
final String lv = "/etc/iiab/local_vars.yml";
761+
String vq = MAPS_VECTOR_OK.contains(mapsVector) ? mapsVector : "osm-z11";
762+
String sat = MAPS_SAT_OK.contains(mapsSat) ? mapsSat : "none";
763+
String ter = MAPS_TERRAIN_OK.contains(mapsTerrain) ? mapsTerrain : "none";
764+
String engine = mapsSearchOn ? "static" : "";
765+
return "sed -i -E '/^[[:space:]]*maps_(install|enabled|region_downloader|vector_quality|" +
766+
"satellite_zoom|terrain_zoom|search_engine|search_static_db|search_nominatim_db|" +
767+
"ne6_zoom|preset_full_quality_regions)[[:space:]]*:/d' " + lv +
768+
" && echo 'maps_install: True' >> " + lv +
769+
" && echo 'maps_enabled: True' >> " + lv +
770+
" && echo 'maps_region_downloader: True' >> " + lv +
771+
" && echo 'maps_vector_quality: " + vq + "' >> " + lv +
772+
" && echo 'maps_satellite_zoom: " + sat + "' >> " + lv +
773+
" && echo 'maps_terrain_zoom: " + ter + "' >> " + lv +
774+
" && echo 'maps_search_engine: \"" + engine + "\"' >> " + lv +
775+
" && echo 'maps_search_static_db: pop-1k-cities' >> " + lv +
776+
" && echo 'maps_search_nominatim_db: basic' >> " + lv +
777+
" && echo 'maps_ne6_zoom: full' >> " + lv +
778+
" && echo 'maps_preset_full_quality_regions: []' >> " + lv +
779+
" && cd /opt/iiab/iiab && ./runrole maps";
780+
}
781+
720782
private void finishModuleQueue() {
721783
if (finished) return;
722784
finished = true;

‎controller/app/src/main/java/org/iiab/controller/redesign/MapsChooseFragment.java‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,16 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
118118
String[] names = new String[GROUPS.length];
119119
String[] opts = new String[GROUPS.length];
120120
long[] mb = new long[GROUPS.length];
121+
// ADFA-4900: carry the machine level keys (null = "off") so the install can write
122+
// local_vars — aligned to GROUPS order [base, satellite, terrain, search].
123+
String[] levels = new String[GROUPS.length];
121124
for (int gi = 0; gi < GROUPS.length; gi++) {
122125
names[gi] = getString(GROUPS[gi].label);
123126
opts[gi] = getString(GROUPS[gi].opts[selectedIdx[gi]].label);
124127
mb[gi] = selectedMb[gi];
128+
levels[gi] = GROUPS[gi].opts[selectedIdx[gi]].level;
125129
}
126-
((SetupLibraryActivity) getActivity()).openMapsConfirm(names, opts, mb);
130+
((SetupLibraryActivity) getActivity()).openMapsConfirm(names, opts, mb, levels);
127131
}
128132
});
129133

‎controller/app/src/main/java/org/iiab/controller/redesign/MapsConfirmFragment.java‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@
3131

3232
public class MapsConfirmFragment extends Fragment {
3333

34-
private static final String ARG_NAMES = "names", ARG_OPTS = "opts", ARG_MB = "mb";
34+
private static final String ARG_NAMES = "names", ARG_OPTS = "opts", ARG_MB = "mb", ARG_LEVELS = "levels";
3535

36-
public static MapsConfirmFragment newInstance(String[] names, String[] opts, long[] mb) {
36+
public static MapsConfirmFragment newInstance(String[] names, String[] opts, long[] mb, String[] levels) {
3737
MapsConfirmFragment f = new MapsConfirmFragment();
3838
Bundle b = new Bundle();
3939
b.putStringArray(ARG_NAMES, names);
4040
b.putStringArray(ARG_OPTS, opts);
4141
b.putLongArray(ARG_MB, mb);
42+
b.putStringArray(ARG_LEVELS, levels); // ADFA-4900: per-layer machine keys (null = off)
4243
f.setArguments(b);
4344
return f;
4445
}
@@ -72,11 +73,13 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
7273
}
7374
box.addView(row(getString(R.string.k2go_maps_total), "", fmt(total), true));
7475

76+
final String[] levels = a != null ? a.getStringArray(ARG_LEVELS) : null;
77+
7578
Button start = root.findViewById(R.id.k2go_start_btn);
7679
start.setText(getString(R.string.k2go_maps_start_building, fmt(total)));
7780
start.setOnClickListener(v -> {
7881
if (getActivity() instanceof SetupLibraryActivity) {
79-
((SetupLibraryActivity) getActivity()).openMapsPreparing();
82+
((SetupLibraryActivity) getActivity()).openMapsPreparing(levels);
8083
}
8184
});
8285

‎controller/app/src/main/java/org/iiab/controller/redesign/MapsPreparingFragment.java‎

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,17 @@
33
* Name : MapsPreparingFragment.java
44
* Author : AppDevForAll
55
* Copyright : Copyright (c) 2026 AppDevForAll
6-
* Description : ADFA-4848 (slice 3). Maps Preparing. A CONTAINED placeholder spinner
7-
* (independent of the boot/close Lottie) plus a single status line that
8-
* mirrors what the background process is doing, like the boot screen shows the
9-
* current service. No invented progress bar. The status text is MOCK for now
10-
* (cycles the phases); the REST/Ansible backend feeds the real output later.
11-
* "Run in background" leaves it running and returns to the Get More hub.
6+
* Description : ADFA-4848 (slice 3) / ADFA-4900. Maps Preparing. A contained placeholder
7+
* spinner plus a single status line. ADFA-4900: this now drives the REAL install
8+
* — on first entry it starts the maps runrole through the module-queue engine
9+
* (InstallService) with the per-layer selection, and the status line follows the
10+
* real queue state (ModuleQueueRepository), no longer a mock. "Run in background"
11+
* leaves it running and returns to the Get More hub.
1212
* ============================================================================
1313
*/
1414
package org.iiab.controller.redesign;
1515

1616
import android.os.Bundle;
17-
import android.os.Handler;
18-
import android.os.Looper;
1917
import android.view.LayoutInflater;
2018
import android.view.View;
2119
import android.view.ViewGroup;
@@ -26,21 +24,21 @@
2624
import androidx.fragment.app.Fragment;
2725

2826
import org.iiab.controller.R;
27+
import org.iiab.controller.install.presentation.ModuleQueueRepository;
28+
import org.iiab.controller.install.presentation.ModuleQueueState;
2929

3030
public class MapsPreparingFragment extends Fragment {
3131

32-
// Mock phase feed until the backend streams real process text.
33-
private final int[] PHASES = {
34-
R.string.k2go_maps_phase_prepared,
35-
R.string.k2go_maps_phase_downloading,
36-
R.string.k2go_maps_phase_building,
37-
R.string.k2go_maps_phase_finishing,
38-
R.string.k2go_maps_phase_ready,
39-
};
32+
private static final String ARG_LEVELS = "levels";
33+
34+
public static MapsPreparingFragment newInstance(String[] levels) {
35+
MapsPreparingFragment f = new MapsPreparingFragment();
36+
Bundle b = new Bundle();
37+
b.putStringArray(ARG_LEVELS, levels);
38+
f.setArguments(b);
39+
return f;
40+
}
4041

41-
private final Handler main = new Handler(Looper.getMainLooper());
42-
private Runnable tick;
43-
private int step = 0;
4442
private TextView status;
4543

4644
@Nullable
@@ -49,37 +47,37 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
4947
View root = inflater.inflate(R.layout.fragment_k2go_maps_preparing, container, false);
5048

5149
status = root.findViewById(R.id.k2go_prep_status);
50+
status.setText(getString(R.string.k2go_maps_phase_prepared));
5251

5352
// Run in background -> back to the Get More hub (drops the whole Maps flow off the
54-
// back stack), the build keeps going.
53+
// back stack), the build keeps going in the foreground service.
5554
root.findViewById(R.id.k2go_prep_run_bg).setOnClickListener(v -> {
5655
if (getActivity() instanceof SetupLibraryActivity) {
5756
((SetupLibraryActivity) getActivity()).backToGetMoreHub();
5857
}
5958
});
6059

61-
startMock();
62-
return root;
63-
}
60+
// Start the real install only on first entry (not on a config-change recreation, and not
61+
// if a maps job is already running/done from this session).
62+
String[] levels = getArguments() != null ? getArguments().getStringArray(ARG_LEVELS) : null;
63+
if (s == null
64+
&& getActivity() instanceof SetupLibraryActivity
65+
&& !ModuleQueueRepository.get().isRunning()) {
66+
((SetupLibraryActivity) getActivity()).startMapsInstall(levels);
67+
}
6468

65-
private void startMock() {
66-
step = 0;
67-
tick = new Runnable() {
68-
@Override
69-
public void run() {
70-
status.setText(getString(PHASES[step]));
71-
if (step < PHASES.length - 1) {
72-
step++;
73-
main.postDelayed(this, 1500);
74-
}
69+
// Follow the real queue state instead of a mock phase cycle.
70+
ModuleQueueRepository.get().state().observe(getViewLifecycleOwner(), st -> {
71+
if (st == null) return;
72+
if (st.phase == ModuleQueueState.Phase.RUNNING) {
73+
status.setText(getString(R.string.k2go_maps_phase_building));
74+
} else if (st.phase == ModuleQueueState.Phase.DONE) {
75+
status.setText(st.failedModules.isEmpty()
76+
? getString(R.string.k2go_maps_phase_ready)
77+
: getString(R.string.k2go_maps_phase_failed));
7578
}
76-
};
77-
main.post(tick);
78-
}
79+
});
7980

80-
@Override
81-
public void onDestroyView() {
82-
if (tick != null) main.removeCallbacks(tick);
83-
super.onDestroyView();
81+
return root;
8482
}
8583
}

‎controller/app/src/main/java/org/iiab/controller/redesign/SetupLibraryActivity.java‎

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,23 +243,44 @@ public void openMapsChoose() {
243243
.commit();
244244
}
245245

246-
/** ADFA-4848: Choose -> Confirm (breakdown + total + time warning). */
247-
public void openMapsConfirm(String[] names, String[] opts, long[] mb) {
246+
/** ADFA-4848: Choose -> Confirm (breakdown + total + time warning). ADFA-4900: also carries the
247+
* per-layer level keys (aligned to the Choose groups; null = off) so Preparing can install. */
248+
public void openMapsConfirm(String[] names, String[] opts, long[] mb, String[] levels) {
248249
getSupportFragmentManager().beginTransaction()
249-
.replace(R.id.k2go_setup_host, MapsConfirmFragment.newInstance(names, opts, mb))
250+
.replace(R.id.k2go_setup_host, MapsConfirmFragment.newInstance(names, opts, mb, levels))
250251
.addToBackStack("maps_confirm")
251252
.commit();
252253
}
253254

254-
/** ADFA-4848: Confirm -> Preparing (contained placeholder animation + process status; mock
255-
* until the backend). */
256-
public void openMapsPreparing() {
255+
/** ADFA-4848: Confirm -> Preparing. ADFA-4900: Preparing drives the real install (runrole maps
256+
* via the module-queue engine) using the per-layer selection and shows real progress. */
257+
public void openMapsPreparing(String[] levels) {
257258
getSupportFragmentManager().beginTransaction()
258-
.replace(R.id.k2go_setup_host, new MapsPreparingFragment())
259+
.replace(R.id.k2go_setup_host, MapsPreparingFragment.newInstance(levels))
259260
.addToBackStack("maps_preparing")
260261
.commit();
261262
}
262263

264+
/** ADFA-4900: start the maps install through the module-queue engine (a queue of {"maps"} plus
265+
* the per-layer selection). InstallService writes the full maps_* local_vars and runs runrole
266+
* with the shared success/failure verdict, revert-on-fail and observable progress. {@code levels}
267+
* is aligned to the Choose groups [base, satellite, terrain, search]; null = off. */
268+
public void startMapsInstall(String[] levels) {
269+
String base = levels != null && levels.length > 0 && levels[0] != null ? levels[0] : "osm-z11";
270+
String sat = levels != null && levels.length > 1 && levels[1] != null ? levels[1] : "none";
271+
String ter = levels != null && levels.length > 2 && levels[2] != null ? levels[2] : "none";
272+
boolean search = levels != null && levels.length > 3 && levels[3] != null;
273+
Intent i = new Intent(this, InstallService.class);
274+
i.setAction(InstallService.ACTION_START_MODULES);
275+
i.putExtra(InstallService.EXTRA_MODULES, new String[]{"maps"});
276+
i.putExtra(InstallService.EXTRA_MAPS_VECTOR, base);
277+
i.putExtra(InstallService.EXTRA_MAPS_SAT, sat);
278+
i.putExtra(InstallService.EXTRA_MAPS_TERRAIN, ter);
279+
i.putExtra(InstallService.EXTRA_MAPS_SEARCH, search);
280+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) startForegroundService(i);
281+
else startService(i);
282+
}
283+
263284
/** ADFA-4848: "Run in background" from Preparing -> drop the whole Maps flow off the back
264285
* stack and return to the Get More hub; the build keeps running. */
265286
public void backToGetMoreHub() {

‎controller/app/src/main/res/values/strings_k2go.xml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@
395395
<string name="k2go_maps_phase_building" translatable="false">Building the map tiles</string>
396396
<string name="k2go_maps_phase_finishing" translatable="false">Finishing up</string>
397397
<string name="k2go_maps_phase_ready" translatable="false">Ready</string>
398+
<string name="k2go_maps_phase_failed" translatable="false">Couldn\'t build maps — check space and connection, then retry</string>
398399

399400
<!-- ADFA-4849: Wikipedia & ZIM content (WIP; translate in a later l10n pass) -->
400401
<string name="k2go_zim_title" translatable="false">Wikipedia &amp; ZIM content</string>

0 commit comments

Comments
 (0)