Skip to content

Commit 8f8b606

Browse files
Merge pull request #426 from appdevforall/feat/ADFA-5094-picker-loose-aggregate
ADFA-5094: show the offline loose-resource aggregate in the picker
2 parents 6c75550 + 2427592 commit 8f8b606

3 files changed

Lines changed: 93 additions & 3 deletions

File tree

‎controller/app/src/main/java/org/iiab/controller/kolibri/presentation/KolibriTopicsFragment.java‎

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,27 @@ private void render(KolibriTopicsUiState s) {
266266
}
267267
list.addView(nodeRow(rows.get(i), childAncestors));
268268
}
269+
// ADFA-5094: the offline bundle folds a folder's own loose resources into an
270+
// aggregate rather than listing them, so show that as one informational line —
271+
// this is also what keeps a loose-only level from rendering blank. The live
272+
// sources list those leaves individually, so looseResourceCount is 0 and this
273+
// is skipped.
274+
TopicNode here = s.node();
275+
if (here != null && here.looseResourceCount() > 0) {
276+
if (!rows.isEmpty()) {
277+
list.addView(hairline());
278+
}
279+
list.addView(looseRow(here));
280+
}
269281
}
270282

271-
count.setText(getString(R.string.k2go_zc_count_items, s.children().size()));
283+
// ADFA-5094: count the rows the level actually shows — the child folders plus the one
284+
// loose-aggregate line when present — so a loose-only level does not read "0 items".
285+
int shown = s.children().size();
286+
if (s.node() != null && s.node().looseResourceCount() > 0) {
287+
shown++;
288+
}
289+
count.setText(getString(R.string.k2go_zc_count_items, shown));
272290
updateStorage();
273291
}
274292

@@ -424,6 +442,26 @@ private String sizeLabel(TopicNode node) {
424442
return "";
425443
}
426444

445+
/**
446+
* ADFA-5094: the offline summary of a folder's direct loose resources — the leaves the
447+
* bundle folds away. Informational only: they have no ids to pick individually (picking
448+
* the folder brings them), so no checkbox or chevron. Reuses the resource-count string so
449+
* no new translation is needed; the size is language-neutral.
450+
*/
451+
private View looseRow(TopicNode node) {
452+
String text = getString(R.string.k2go_kolibri_resources_fmt, node.looseResourceCount());
453+
if (node.looseResourceBytes() > 0) {
454+
text = text + " · " + ByteFormatter.toHuman(node.looseResourceBytes());
455+
}
456+
TextView t = new TextView(requireContext());
457+
t.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodyMedium);
458+
t.setText(text);
459+
t.setTextColor(ContextCompat.getColor(requireContext(), R.color.k2go_muted));
460+
t.setPadding(px(12), px(10), px(12), px(10));
461+
t.setMinimumHeight(px(36));
462+
return t;
463+
}
464+
427465
private View note(String text, int colorRes) {
428466
TextView t = new TextView(requireContext());
429467
t.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodySmall);

‎controller/app/src/main/java/org/iiab/controller/kolibri/presentation/KolibriTopicsUiState.java‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,14 @@ public List<TopicNode> children() {
9292
return node == null ? Collections.<TopicNode>emptyList() : node.children();
9393
}
9494

95-
/** True when the level arrived and genuinely has nothing under it. */
95+
/**
96+
* True when the level arrived and genuinely has nothing under it — no child folders and
97+
* no loose resources folded in by the offline bundle (ADFA-5094). A level with only loose
98+
* content is not empty: it renders the aggregate line, not the "empty" status.
99+
*/
96100
public boolean isEmpty() {
97-
return !loading && !unavailable && (node == null || !node.hasChildren());
101+
return !loading && !unavailable
102+
&& (node == null || (!node.hasChildren() && node.looseResourceCount() == 0));
98103
}
99104

100105
/** What to show as the screen's heading: the channel, or the topic drilled into. */
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package org.iiab.controller.kolibri.presentation;
2+
3+
import static org.junit.Assert.assertFalse;
4+
import static org.junit.Assert.assertTrue;
5+
6+
import org.iiab.controller.kolibri.domain.TopicNode;
7+
import org.junit.Test;
8+
9+
import java.util.Collections;
10+
11+
/**
12+
* Unit tests for {@link KolibriTopicsUiState#isEmpty()} — the call that decides whether a
13+
* level renders content or the "empty" status. The offline bundle folds a folder's loose
14+
* leaves into an aggregate, so "no child folders" no longer means "nothing here" (ADFA-5094).
15+
* Pure JVM, no Android.
16+
*/
17+
public class KolibriTopicsUiStateTest {
18+
19+
private static final String ROOT = "c150ea1d69495d37b5b0ac6f017e9bfb";
20+
private static final String CHILD = "23a7dc9c73635cd2abbd3e8aab13c3ca";
21+
22+
private static KolibriTopicsUiState levelOf(TopicNode node) {
23+
return KolibriTopicsUiState.level(node, "Folder",
24+
Collections.<String>emptyList(), Collections.singletonList("Folder"));
25+
}
26+
27+
@Test
28+
public void aLevelWithOnlyLooseResourcesIsNotEmpty() {
29+
// No sub-folders, but loose resources: must render the aggregate line, not "empty".
30+
TopicNode loose = TopicNode.fromBundle(ROOT, "Folder", "topic", 100L, 3, 3, 100L, null);
31+
assertFalse(levelOf(loose).isEmpty());
32+
}
33+
34+
@Test
35+
public void aLevelWithNeitherChildrenNorLooseIsEmpty() {
36+
TopicNode bare = TopicNode.fromBundle(ROOT, "Folder", "topic", 0L, 0, 0, 0L, null);
37+
assertTrue(levelOf(bare).isEmpty());
38+
}
39+
40+
@Test
41+
public void aLevelWithChildFoldersIsNotEmpty() {
42+
TopicNode child = TopicNode.fromBundle(CHILD, "Sub", "topic", 0L, 0, 0, 0L, null);
43+
TopicNode parent = TopicNode.fromBundle(ROOT, "Folder", "topic", 0L, 1, 0, 0L,
44+
Collections.singletonList(child));
45+
assertFalse(levelOf(parent).isEmpty());
46+
}
47+
}

0 commit comments

Comments
 (0)