Skip to content

Commit 1099901

Browse files
Merge pull request #274 from appdevforall/feat/ADFA-4901-maps-card-detail
ADFA-4901: open the maps progress detail from the finishing-setup card
2 parents 290f2b3 + c0f4be4 commit 1099901

6 files changed

Lines changed: 186 additions & 18 deletions

File tree

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,9 @@ public android.graphics.Typeface fetchFont(String fontFamily) {
166166
// ADFA-4853: if the wizard banked content, go straight to Finishing setup
167167
// (over the library) — no brief stop on the home. That screen shows
168168
// "Starting services…" and drains the wishlists when the engine is up.
169-
if (BooksWishlist.size(this) > 0 || ZimWishlist.size(this) > 0) {
169+
// ADFA-4901: include MapsWishlist — a maps-only wizard run must still open
170+
// Finishing setup so MapsProvisioner can drain it (otherwise maps never installs).
171+
if (BooksWishlist.size(this) > 0 || ZimWishlist.size(this) > 0 || MapsWishlist.has(this)) {
170172
startActivity(new android.content.Intent(this, SetupProgressActivity.class));
171173
}
172174
new Handler(Looper.getMainLooper()).postDelayed(() -> {

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

Lines changed: 105 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,26 @@
1717
import android.view.LayoutInflater;
1818
import android.view.View;
1919
import android.view.ViewGroup;
20+
import android.widget.ImageView;
21+
import android.widget.ScrollView;
2022
import android.widget.TextView;
2123

2224
import androidx.annotation.NonNull;
2325
import androidx.annotation.Nullable;
2426
import androidx.fragment.app.Fragment;
2527

28+
import java.util.ArrayDeque;
29+
import java.util.List;
30+
31+
import org.iiab.controller.LogRepository;
2632
import org.iiab.controller.R;
2733
import org.iiab.controller.install.presentation.ModuleQueueRepository;
2834
import org.iiab.controller.install.presentation.ModuleQueueState;
2935

3036
public class MapsPreparingFragment extends Fragment {
3137

3238
private static final String ARG_LEVELS = "levels";
39+
private static final String ARG_FROM_INDEX = "fromIndex";
3340

3441
public static MapsPreparingFragment newInstance(String[] levels) {
3542
MapsPreparingFragment f = new MapsPreparingFragment();
@@ -39,48 +46,134 @@ public static MapsPreparingFragment newInstance(String[] levels) {
3946
return f;
4047
}
4148

49+
/** ADFA-4901: open as the maps detail inside the Finishing-setup index — observe only, never
50+
* (re)start the install, and hide this fragment's own "Run in background" button (the index
51+
* host provides Back/Finish). Mirrors ZimPreparingFragment.newInstance(fromIndex). */
52+
public static MapsPreparingFragment newInstance(boolean fromIndex) {
53+
MapsPreparingFragment f = new MapsPreparingFragment();
54+
Bundle b = new Bundle();
55+
b.putBoolean(ARG_FROM_INDEX, fromIndex);
56+
f.setArguments(b);
57+
return f;
58+
}
59+
4260
private TextView status;
61+
private boolean fromIndex = false; // ADFA-4901: hosted by the Finishing-setup index (observe only)
4362
private boolean launched = false; // ADFA-4900: guard against re-launching maps on view recreation
4463

64+
// ADFA-4901: collapsible live log (LogRepository) — the reusable terminal for proot module installs.
65+
private static final int MAX_LOG_LINES = 500; // bound the on-screen buffer (repo keeps more)
66+
private TextView logText, logLabel;
67+
private ScrollView logScroll;
68+
private ImageView logChevron;
69+
private boolean logExpanded = false;
70+
private boolean terminal = false; // true once the queue reports DONE (stop live status override)
71+
private final ArrayDeque<String> logLines = new ArrayDeque<>();
72+
private LogRepository.Listener logListener;
73+
4574
@Nullable
4675
@Override
4776
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle s) {
4877
View root = inflater.inflate(R.layout.fragment_k2go_maps_preparing, container, false);
4978

79+
fromIndex = getArguments() != null && getArguments().getBoolean(ARG_FROM_INDEX, false);
80+
5081
status = root.findViewById(R.id.k2go_prep_status);
5182
status.setText(getString(R.string.k2go_maps_phase_prepared));
5283

53-
// Run in background -> back to the Get More hub (drops the whole Maps flow off the
54-
// back stack), the build keeps going in the foreground service.
55-
root.findViewById(R.id.k2go_prep_run_bg).setOnClickListener(v -> {
56-
if (getActivity() instanceof SetupLibraryActivity) {
57-
((SetupLibraryActivity) getActivity()).backToGetMoreHub();
58-
}
59-
});
84+
View runBg = root.findViewById(R.id.k2go_prep_run_bg);
85+
if (fromIndex) {
86+
runBg.setVisibility(View.GONE); // the index host provides Back/Finish; this card only observes
87+
} else {
88+
// Run in background -> back to the Get More hub (drops the whole Maps flow off the
89+
// back stack), the build keeps going in the foreground service.
90+
runBg.setOnClickListener(v -> {
91+
if (getActivity() instanceof SetupLibraryActivity) {
92+
((SetupLibraryActivity) getActivity()).backToGetMoreHub();
93+
}
94+
});
95+
}
6096

61-
// Start the real install only on first entry (not on a config-change recreation, and not
62-
// if a maps job is already running/done from this session).
97+
// Start the real install only on first entry (not on a config-change recreation, not when
98+
// hosted as the Finishing-setup detail, and not if a maps job is already running/done).
6399
String[] levels = getArguments() != null ? getArguments().getStringArray(ARG_LEVELS) : null;
64-
if (s == null && !launched
100+
if (s == null && !launched && !fromIndex
65101
&& getActivity() instanceof SetupLibraryActivity
66102
&& !ModuleQueueRepository.get().isRunning()
67103
&& ModuleQueueRepository.get().current().phase != ModuleQueueState.Phase.DONE) {
68104
((SetupLibraryActivity) getActivity()).startMapsInstall(levels);
69105
launched = true;
70106
}
71107

72-
// Follow the real queue state instead of a mock phase cycle.
108+
// Follow the real queue state. While RUNNING the status line is driven by the live log
109+
// (latest line); the phase text is only a fallback until the first line arrives. DONE is
110+
// terminal and always wins.
73111
ModuleQueueRepository.get().state().observe(getViewLifecycleOwner(), st -> {
74112
if (st == null) return;
75113
if (st.phase == ModuleQueueState.Phase.RUNNING) {
76-
status.setText(getString(R.string.k2go_maps_phase_building));
114+
if (logLines.isEmpty()) status.setText(getString(R.string.k2go_maps_phase_building));
77115
} else if (st.phase == ModuleQueueState.Phase.DONE) {
116+
terminal = true;
78117
status.setText(st.failedModules.isEmpty()
79118
? getString(R.string.k2go_maps_phase_ready)
80119
: getString(R.string.k2go_maps_phase_failed));
81120
}
82121
});
83122

123+
// ADFA-4901: collapsible live log. The status line mirrors the latest line; expanding shows
124+
// the full terminal (LogRepository snapshot + live appends), auto-scrolled.
125+
logLabel = root.findViewById(R.id.k2go_prep_log_label);
126+
logChevron = root.findViewById(R.id.k2go_prep_log_chevron);
127+
logScroll = root.findViewById(R.id.k2go_prep_log_scroll);
128+
logText = root.findViewById(R.id.k2go_prep_log_text);
129+
root.findViewById(R.id.k2go_prep_log_toggle).setOnClickListener(v -> toggleLog());
130+
131+
logListener = new LogRepository.Listener() {
132+
@Override public void onAppend(String line) {
133+
if (!isAdded() || line == null) return;
134+
if (!terminal) status.setText(line);
135+
if (logExpanded) { pushLine(line); renderLog(); }
136+
}
137+
@Override public void onCleared() {
138+
logLines.clear();
139+
if (logExpanded) renderLog();
140+
}
141+
};
142+
LogRepository.get().addListener(logListener);
143+
84144
return root;
85145
}
146+
147+
/** Expand/collapse the terminal. On expand, seed from the current LogRepository snapshot. */
148+
private void toggleLog() {
149+
logExpanded = !logExpanded;
150+
logLabel.setText(getString(logExpanded ? R.string.k2go_maps_log_hide : R.string.k2go_maps_log_show));
151+
logChevron.setRotation(logExpanded ? 90f : 0f);
152+
logScroll.setVisibility(logExpanded ? View.VISIBLE : View.GONE);
153+
if (logExpanded) {
154+
logLines.clear();
155+
List<String> snap = LogRepository.get().snapshot();
156+
int start = Math.max(0, snap.size() - MAX_LOG_LINES);
157+
for (int i = start; i < snap.size(); i++) logLines.addLast(snap.get(i));
158+
renderLog();
159+
}
160+
}
161+
162+
private void pushLine(String line) {
163+
logLines.addLast(line);
164+
while (logLines.size() > MAX_LOG_LINES) logLines.removeFirst();
165+
}
166+
167+
private void renderLog() {
168+
StringBuilder sb = new StringBuilder();
169+
for (String l : logLines) sb.append(l).append('\n');
170+
logText.setText(sb.toString());
171+
logScroll.post(() -> logScroll.fullScroll(View.FOCUS_DOWN));
172+
}
173+
174+
@Override
175+
public void onDestroyView() {
176+
if (logListener != null) LogRepository.get().removeListener(logListener);
177+
super.onDestroyView();
178+
}
86179
}

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

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -324,9 +324,9 @@ private View streamRow(String heading, String key, boolean sess, int[] status, i
324324
return row;
325325
}
326326

327-
/** ADFA-4900: summary row for the maps (proot) stage, driven by the module-queue state:
328-
* queued -> spinner (runrole in flight) -> check (done) / amber alert (failed). No detail
329-
* screen yet (the richer downloads card is ADFA-4901), so the row is not tappable. */
327+
/** ADFA-4900/4901: summary row for the maps (proot) stage, driven by the module-queue state:
328+
* queued -> spinner (runrole in flight) -> check (done) / amber alert (failed). Tappable once
329+
* the stage has started; opens the maps Preparing card as its detail (ADFA-4901). */
330330
private View mapsRow() {
331331
ModuleQueueState mq = ModuleQueueRepository.get().current();
332332
boolean done = mapsStartFailed || (mapsLaunched && mq.phase == ModuleQueueState.Phase.DONE);
@@ -371,6 +371,16 @@ private View mapsRow() {
371371
sub.setTextColor(ContextCompat.getColor(this, failed ? R.color.k2go_amber_text : R.color.k2go_muted));
372372
col.addView(sub);
373373
row.addView(col, new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f));
374+
375+
// ADFA-4901: like the ZIM/Books rows, maps opens its own progress detail once the stage has
376+
// started (running or done). Maps is a single proot runrole, so the detail is the queue-driven
377+
// Preparing card (spinner + phase), not a per-item checklist.
378+
ImageView chev = new ImageView(this);
379+
chev.setImageResource(R.drawable.ic_chevron_right);
380+
chev.setColorFilter(ContextCompat.getColor(this, R.color.k2go_muted));
381+
chev.setVisibility(started ? View.VISIBLE : View.INVISIBLE);
382+
row.addView(chev, new LinearLayout.LayoutParams(px(24), px(24)));
383+
if (started) row.setOnClickListener(v -> openDetail("maps"));
374384
return row;
375385
}
376386

@@ -421,8 +431,10 @@ private void goHome(boolean clearSessions) {
421431
// ---- detail: host the real per-module card ----
422432
private void openDetail(String key) {
423433
showingDetail = true;
424-
androidx.fragment.app.Fragment f = "zim".equals(key)
425-
? ZimPreparingFragment.newInstance(true) : BooksDownloadsFragment.newInstance(true);
434+
androidx.fragment.app.Fragment f;
435+
if ("zim".equals(key)) f = ZimPreparingFragment.newInstance(true);
436+
else if ("maps".equals(key)) f = MapsPreparingFragment.newInstance(true); // ADFA-4901: observe-only
437+
else f = BooksDownloadsFragment.newInstance(true);
426438
getSupportFragmentManager().beginTransaction().replace(R.id.k2go_sp_fraghost, f).commit();
427439
indexScroll.setVisibility(View.GONE);
428440
detailRoot.setVisibility(View.VISIBLE);

‎controller/app/src/main/res/layout/fragment_k2go_maps_preparing.xml‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
background process is doing (like the boot screen shows the current service). No invented
55
progress bar. Mock text until the REST/Ansible backend feeds real output. -->
66
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
7+
xmlns:app="http://schemas.android.com/apk/res-auto"
78
android:layout_width="match_parent"
89
android:layout_height="match_parent"
910
android:fillViewport="true">
@@ -55,6 +56,8 @@
5556
android:layout_height="wrap_content"
5657
android:layout_marginTop="8dp"
5758
android:gravity="center"
59+
android:maxLines="2"
60+
android:ellipsize="end"
5861
android:text="@string/k2go_maps_phase_prepared"
5962
android:textAppearance="?attr/textAppearanceBodyLarge"
6063
android:textColor="@color/k2go_ink" />
@@ -86,6 +89,57 @@
8689
android:textColor="@color/k2go_teal" />
8790
</LinearLayout>
8891

92+
<!-- ADFA-4901: collapsible live log fed by LogRepository (the reusable terminal for all
93+
proot module installs). Hidden by default; the status line above shows the latest line. -->
94+
<LinearLayout
95+
android:id="@+id/k2go_prep_log_toggle"
96+
android:layout_width="match_parent"
97+
android:layout_height="wrap_content"
98+
android:layout_marginTop="14dp"
99+
android:orientation="horizontal"
100+
android:gravity="center_vertical"
101+
android:padding="12dp"
102+
android:background="@drawable/k2go_getmore_bg"
103+
android:clickable="true"
104+
android:focusable="true">
105+
106+
<TextView
107+
android:id="@+id/k2go_prep_log_label"
108+
android:layout_width="0dp"
109+
android:layout_height="wrap_content"
110+
android:layout_weight="1"
111+
android:text="@string/k2go_maps_log_show"
112+
android:textAppearance="?attr/textAppearanceBodyMedium"
113+
android:textColor="@color/k2go_ink" />
114+
115+
<ImageView
116+
android:id="@+id/k2go_prep_log_chevron"
117+
android:layout_width="24dp"
118+
android:layout_height="24dp"
119+
android:src="@drawable/ic_chevron_right"
120+
app:tint="@color/k2go_muted"
121+
android:importantForAccessibility="no" />
122+
</LinearLayout>
123+
124+
<ScrollView
125+
android:id="@+id/k2go_prep_log_scroll"
126+
android:layout_width="match_parent"
127+
android:layout_height="180dp"
128+
android:layout_marginTop="8dp"
129+
android:padding="10dp"
130+
android:visibility="gone"
131+
android:background="@color/k2go_terminal_bg">
132+
133+
<TextView
134+
android:id="@+id/k2go_prep_log_text"
135+
android:layout_width="match_parent"
136+
android:layout_height="wrap_content"
137+
android:fontFamily="monospace"
138+
android:textSize="11sp"
139+
android:textColor="@color/k2go_terminal_text"
140+
android:textIsSelectable="true" />
141+
</ScrollView>
142+
89143
<Button
90144
android:id="@+id/k2go_prep_run_bg"
91145
android:layout_width="match_parent"

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
<color name="k2go_amber_text">#B0740F</color>
2020
<color name="k2go_clay">#C4462F</color>
2121
<color name="k2go_notice_ink">#3A2320</color> <!-- fixed dark; notice bg is always light pink -->
22+
<!-- ADFA-4901: install log terminal — fixed dark surface + light-green text in both themes
23+
(reused by all proot module install logs). -->
24+
<color name="k2go_terminal_bg">#0F1512</color>
25+
<color name="k2go_terminal_text">#9FE1CB</color>
2226
<color name="k2go_muted">#5C6360</color>
2327
<color name="k2go_hairline">#D9D4C8</color>
2428
<color name="k2go_on_teal">#FBF8F3</color>

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,9 @@
391391
<string name="k2go_maps_leave_title" translatable="false">Leave it running</string>
392392
<string name="k2go_maps_leave_body" translatable="false">It continues in the background. We\u2019ll notify you when your maps are ready.</string>
393393
<string name="k2go_maps_run_bg" translatable="false">Run in background</string>
394+
<!-- ADFA-4901: collapsible install log (reusable across proot modules). -->
395+
<string name="k2go_maps_log_show" translatable="false">Show details</string>
396+
<string name="k2go_maps_log_hide" translatable="false">Hide details</string>
394397
<string name="k2go_maps_phase_prepared" translatable="false">Prepared the list</string>
395398
<string name="k2go_maps_phase_downloading" translatable="false">Downloading map data</string>
396399
<string name="k2go_maps_phase_building" translatable="false">Building the map tiles</string>

0 commit comments

Comments
 (0)