diff --git a/controller/app/src/main/java/org/appdevforall/k2go/kolibri/presentation/KolibriBrowseFragment.java b/controller/app/src/main/java/org/appdevforall/k2go/kolibri/presentation/KolibriBrowseFragment.java
index d3e1b0640..12c6532e1 100644
--- a/controller/app/src/main/java/org/appdevforall/k2go/kolibri/presentation/KolibriBrowseFragment.java
+++ b/controller/app/src/main/java/org/appdevforall/k2go/kolibri/presentation/KolibriBrowseFragment.java
@@ -37,6 +37,7 @@
import org.appdevforall.k2go.R;
import org.appdevforall.k2go.applang.data.ContentLanguage;
import org.appdevforall.k2go.kolibri.domain.Channel;
+import org.appdevforall.k2go.redesign.K2GoFilterChip;
import org.appdevforall.k2go.redesign.SetupLibraryActivity;
import org.appdevforall.k2go.redesign.ZimLanguageDialog;
import org.appdevforall.k2go.util.ByteFormatter;
@@ -80,8 +81,8 @@ public final class KolibriBrowseFragment extends Fragment {
private TextView storageLabel;
private ProgressBar storageBar;
private LinearLayout chips;
- private TextView sortSize;
- private TextView sortName;
+ private com.google.android.material.chip.Chip sortSize;
+ private com.google.android.material.chip.Chip sortName;
private LinearLayout list;
private Button review;
@@ -281,11 +282,10 @@ private void updateSortControls() {
pill(sortName, s.isName());
}
- /** The one pill style in the app: teal filled when on, hairline outline when off. */
- private void pill(TextView t, boolean on) {
- t.setBackgroundResource(on ? R.drawable.k2go_chip_bg : R.drawable.k2go_pill_bg);
- t.setTextColor(ContextCompat.getColor(requireContext(),
- on ? android.R.color.white : R.color.k2go_ink));
+ // K2GO-385 (PR3): the shared filter chip (8dp, 32dp, check when active) -- the sort toggles now match
+ // the ZIM sort chips and the category/Books filters; the label still carries the sort direction.
+ private void pill(com.google.android.material.chip.Chip t, boolean on) {
+ K2GoFilterChip.style(t, on);
}
/**
@@ -456,25 +456,19 @@ private void buildChips(KolibriCatalogUiState s) {
}
private View chip(String label, final String group) {
- TextView t = new TextView(requireContext());
- t.setText(label);
- t.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodySmall);
- t.setGravity(Gravity.CENTER);
- t.setMinHeight(px(48)); // tap target, even though the pill looks smaller
- t.setPadding(px(14), px(8), px(14), px(8));
- pill(t, group.equals(groupFilter));
-
+ // K2GO-385 (PR3): category chips use the shared filter chip too.
+ com.google.android.material.chip.Chip c = K2GoFilterChip.create(requireContext(), label,
+ group.equals(groupFilter), x -> {
+ if (!group.equals(groupFilter)) {
+ groupFilter = group;
+ render(vm.state().getValue());
+ }
+ });
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
lp.rightMargin = px(8);
- t.setLayoutParams(lp);
- t.setOnClickListener(x -> {
- if (!group.equals(groupFilter)) {
- groupFilter = group;
- render(vm.state().getValue());
- }
- });
- return t;
+ c.setLayoutParams(lp);
+ return c;
}
private void updateStorage() {
diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/BooksLandingFragment.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/BooksLandingFragment.java
index 8bc56283e..c884fef7d 100644
--- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/BooksLandingFragment.java
+++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/BooksLandingFragment.java
@@ -37,6 +37,8 @@
import androidx.fragment.app.Fragment;
import org.appdevforall.k2go.PortalActivity;
+import com.google.android.material.chip.Chip;
+
import org.appdevforall.k2go.R;
import org.appdevforall.k2go.config.BoxEndpoints;
import org.json.JSONArray;
@@ -200,22 +202,10 @@ private void openLanguagePicker() {
() -> { lang = ""; updateLangPill(); loadBooks(); });
}
- private TextView chip(String text, boolean on, Runnable onClick) {
- TextView t = new TextView(requireContext());
- t.setText(text);
- t.setPadding(px(14), px(8), px(14), px(8));
- t.setBackgroundResource(on ? R.drawable.k2go_chip_bg : R.drawable.k2go_pill_bg);
- // ADFA-5248: apply the text appearance FIRST, then the color. TextAppearance_Material3_*
- // carries its own colorOnSurface, so setting the color before it silently overwrote the chip
- // color (the real bug the ticket reported: onSurface flips with the theme, giving dark-on-
- // dark-teal in light mode and light-on-aqua in dark mode — never legible on the teal fill).
- // With the order fixed, k2go_on_teal sticks: it flips against the fill (light text on the
- // dark-teal light-mode fill, dark text on the light-aqua dark-mode fill).
- t.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodySmall);
- t.setTextColor(ContextCompat.getColor(requireContext(), on ? R.color.k2go_on_teal : R.color.k2go_ink));
- t.setClickable(true);
- t.setOnClickListener(v -> onClick.run());
- return t;
+ // K2GO-385 (PR3): the shared filter chip (8dp corner, 32dp, check when selected). Replaces the
+ // per-screen k2go_chip_bg/k2go_pill_bg drawable pair that had drifted from the other filter surfaces.
+ private Chip chip(String text, boolean on, Runnable onClick) {
+ return K2GoFilterChip.create(requireContext(), text, on, v -> onClick.run());
}
private void loadLibrary() {
diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/FqrController.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/FqrController.java
index 42d8c622e..ab42de623 100644
--- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/FqrController.java
+++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/FqrController.java
@@ -443,13 +443,15 @@ private void showOverlay(String name, long sizeBytes) {
// ADFA-4896: Stop/Retry beside Cancel. The label follows the reported state; the tap fires the
// matching verb and the poll (onPaused/onProgress) is the source of truth.
overlayStopped = false;
- overlayStop = new MaterialButton(themed, null,
- com.google.android.material.R.attr.materialButtonOutlinedStyle);
+ // K2GO-385 (PR3): the download controls use the app button system (K2Go outlined stadium) via the
+ // shared overlay, not a bare Material3 outlined button. FQR's overlay is a themed (day/night)
+ // surface, so the K2Go outlined style's theme teal is right here -- not the fixed boot tokens.
+ ContextThemeWrapper btnCtx = new ContextThemeWrapper(themed, R.style.ThemeOverlay_K2Go_Button_Outlined);
+ overlayStop = new MaterialButton(btnCtx, null);
overlayStop.setText(R.string.k2go_clone_stop_confirm);
overlayStop.setOnClickListener(v -> toggleStop());
row.addView(overlayStop);
- MaterialButton cancel = new MaterialButton(themed, null,
- com.google.android.material.R.attr.materialButtonOutlinedStyle);
+ MaterialButton cancel = new MaterialButton(btnCtx, null);
cancel.setText(R.string.k2go_cancel);
cancel.setOnClickListener(v -> { client.cancel(); hideOverlay(); });
row.addView(cancel);
diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/K2GoFilterChip.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/K2GoFilterChip.java
new file mode 100644
index 000000000..5179e8ec2
--- /dev/null
+++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/K2GoFilterChip.java
@@ -0,0 +1,75 @@
+/*
+ * ============================================================================
+ * Name : K2GoFilterChip.java
+ * Author : AppDevForAll
+ * Copyright : Copyright (c) 2026 AppDevForAll
+ * Description : K2GO-385 (PR3). The shared FILTER CHIP (pill-roles design decision, board
+ * k2go-chip-vs-button-v1): a selectable/toggle chip -- Popular / Educational, a ZIM
+ * category, a sort order. Per the decision it is an 8dp-corner, ~32dp Material 3 chip
+ * with a leading check when selected -- "8dp corner + check = toggle", distinct on
+ * purpose from the stadium action button, the dot+text status and the 8dp metadata
+ * tag. Built ONCE here instead of the three drifted per-screen chip builders it
+ * replaces (ZimLanding / ZimCategory / BooksLanding). Colour is the app teal (filled +
+ * on-teal text when selected; transparent + teal outline + teal text when not). Pure
+ * UI; no domain/data dependencies.
+ * ============================================================================
+ */
+package org.appdevforall.k2go.redesign;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.view.View;
+
+import androidx.core.content.ContextCompat;
+
+import com.google.android.material.chip.Chip;
+
+import org.appdevforall.k2go.R;
+
+public final class K2GoFilterChip {
+
+ private K2GoFilterChip() {}
+
+ /**
+ * Build a selectable filter chip. {@code selected} sets the checked look (teal fill + check);
+ * {@code onClick} fires on tap -- the caller owns the selection model (usually rebuilding the
+ * row), so the chip is a view of that state, not the source of truth.
+ */
+ public static Chip create(Context ctx, CharSequence label, boolean selected, View.OnClickListener onClick) {
+ Chip chip = new Chip(ctx);
+ chip.setText(label);
+ style(chip, selected);
+ chip.setOnClickListener(onClick);
+ return chip;
+ }
+
+ /**
+ * Apply the filter-chip look + selected state to an EXISTING chip (e.g. one inflated from XML,
+ * like the ZimCategory sort chips). Idempotent -- safe to call on every render. The caller owns
+ * the text (so a sort chip can carry its "By size ▲" direction) and the click.
+ */
+ public static void style(Chip chip, boolean selected) {
+ Context ctx = chip.getContext();
+ float d = ctx.getResources().getDisplayMetrics().density;
+ chip.setCheckable(true);
+ chip.setChecked(selected);
+ chip.setCheckedIconVisible(true); // the "check" cue the design calls for
+ // A bare new Chip(ctx) picks up the theme's default (Assist) chipStyle, which has NO checkedIcon,
+ // so set a plain check explicitly -- otherwise setCheckedIconVisible shows nothing.
+ chip.setCheckedIcon(ContextCompat.getDrawable(ctx, R.drawable.ic_check_16));
+ chip.setChipCornerRadius(8 * d); // 8dp corner = chip/toggle (not a stadium pill)
+ chip.setChipMinHeight(32 * d); // the 32dp step on the 4dp role ladder
+ chip.setEnsureMinTouchTargetSize(true); // keep a >=48dp touch target on the 32dp chip
+ chip.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_LabelLarge);
+
+ int teal = ContextCompat.getColor(ctx, R.color.k2go_teal);
+ int onTeal = ContextCompat.getColor(ctx, R.color.k2go_on_teal);
+ int[][] states = { new int[]{ android.R.attr.state_checked }, new int[0] };
+ chip.setChipBackgroundColor(new ColorStateList(states, new int[]{ teal, Color.TRANSPARENT }));
+ chip.setChipStrokeColor(new ColorStateList(states, new int[]{ Color.TRANSPARENT, teal }));
+ chip.setChipStrokeWidth(Math.max(1, Math.round(1.4f * d)));
+ chip.setTextColor(new ColorStateList(states, new int[]{ onTeal, teal }));
+ chip.setCheckedIconTint(ColorStateList.valueOf(onTeal));
+ }
+}
diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimCategoryFragment.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimCategoryFragment.java
index a8ea53d2d..2477eedab 100644
--- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimCategoryFragment.java
+++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimCategoryFragment.java
@@ -71,7 +71,8 @@ private static final class Entry {
private long freeMb = 0, totalMb = 0;
private LinearLayout list;
- private TextView freeLabel, sortSize, sortName, sortGroup, count, langCurrent, langSub;
+ private TextView freeLabel, count, langCurrent, langSub;
+ private com.google.android.material.chip.Chip sortSize, sortName, sortGroup;
private ProgressBar bar;
private Button add;
@@ -275,10 +276,11 @@ private void addHeader(String title, String desc) {
list.addView(h);
}
- private void chip(TextView t, boolean on) {
- t.setBackgroundResource(on ? R.drawable.k2go_chip_bg : R.drawable.k2go_pill_bg);
- // ADFA-4910: white on the selected (teal) chip (same fix as Books / MapsChoose).
- t.setTextColor(ContextCompat.getColor(requireContext(), on ? android.R.color.white : R.color.k2go_ink));
+ // K2GO-385 (PR3): the sort chips use the one shared filter chip (8dp, 32dp, check when active) so
+ // they stop looking different from the category/Books filter chips; the label still carries the
+ // sort direction (e.g. "By size ▲").
+ private void chip(com.google.android.material.chip.Chip t, boolean on) {
+ K2GoFilterChip.style(t, on);
}
// ADFA-5033: flat list row (spec §10) — a simple line with a hairline between rows, NOT a card.
diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimLandingFragment.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimLandingFragment.java
index 4872bc334..8c872b687 100644
--- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimLandingFragment.java
+++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimLandingFragment.java
@@ -28,6 +28,8 @@
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
+import com.google.android.material.chip.Chip;
+
import org.appdevforall.k2go.R;
import org.appdevforall.k2go.applang.data.ContentLanguage;
import org.json.JSONObject;
@@ -253,36 +255,19 @@ private void buildChips() {
private View chip(String label, String groupKey) {
boolean selected = (groupKey == null) ? (selectedGroup == null) : groupKey.equals(selectedGroup);
- int teal = ContextCompat.getColor(requireContext(), R.color.k2go_teal);
- TextView t = new TextView(requireContext());
- t.setText(label);
- t.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_LabelLarge);
- t.setGravity(Gravity.CENTER);
- t.setMinHeight(px(48)); // ADFA-5033: ≥48dp tap target (spec §9)
- t.setPadding(px(14), px(6), px(14), px(6));
- android.graphics.drawable.GradientDrawable bg = new android.graphics.drawable.GradientDrawable();
- bg.setShape(android.graphics.drawable.GradientDrawable.RECTANGLE);
- bg.setCornerRadius(px(24)); // full pill at 48dp
- if (selected) {
- bg.setColor(teal);
- t.setTextColor(android.graphics.Color.WHITE);
- } else {
- bg.setColor(android.graphics.Color.TRANSPARENT);
- bg.setStroke(Math.max(1, Math.round(1.4f * getResources().getDisplayMetrics().density)), teal);
- t.setTextColor(teal);
- }
- t.setBackground(bg);
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
- lp.rightMargin = px(8);
- t.setLayoutParams(lp);
- t.setOnClickListener(v -> {
+ // K2GO-385 (PR3): one shared filter chip (8dp corner, 32dp, check when selected) -- replaces the
+ // per-screen 48dp teal pill that had drifted from the other filter surfaces.
+ Chip c = K2GoFilterChip.create(requireContext(), label, selected, v -> {
selectedGroup = groupKey;
expanded = false;
buildChips();
buildRows();
});
- return t;
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+ lp.rightMargin = px(8);
+ c.setLayoutParams(lp);
+ return c;
}
/** Teal caps section header (MOST CONTENT / group headers). */
diff --git a/controller/app/src/main/res/drawable/ic_check_16.xml b/controller/app/src/main/res/drawable/ic_check_16.xml
new file mode 100644
index 000000000..c9610dda7
--- /dev/null
+++ b/controller/app/src/main/res/drawable/ic_check_16.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/controller/app/src/main/res/layout/activity_library.xml b/controller/app/src/main/res/layout/activity_library.xml
index 872e01615..a165bc0b7 100644
--- a/controller/app/src/main/res/layout/activity_library.xml
+++ b/controller/app/src/main/res/layout/activity_library.xml
@@ -201,22 +201,17 @@
android:visibility="gone">
+ android:minWidth="132dp" />
+ android:text="@string/k2go_dl_cancel" />
diff --git a/controller/app/src/main/res/layout/fragment_k2go_kolibri_browse.xml b/controller/app/src/main/res/layout/fragment_k2go_kolibri_browse.xml
index f95863a35..a09bed5af 100644
--- a/controller/app/src/main/res/layout/fragment_k2go_kolibri_browse.xml
+++ b/controller/app/src/main/res/layout/fragment_k2go_kolibri_browse.xml
@@ -133,26 +133,16 @@
android:layout_marginTop="12dp"
android:orientation="horizontal">
-
+ android:layout_height="wrap_content" />
-
+ android:layout_marginStart="8dp" />
diff --git a/controller/app/src/main/res/layout/fragment_k2go_zim_category.xml b/controller/app/src/main/res/layout/fragment_k2go_zim_category.xml
index 158af94ef..4b08cd981 100644
--- a/controller/app/src/main/res/layout/fragment_k2go_zim_category.xml
+++ b/controller/app/src/main/res/layout/fragment_k2go_zim_category.xml
@@ -100,23 +100,17 @@
android:layout_marginTop="12dp"
android:orientation="horizontal">
-
+
-
+ android:layout_marginStart="8dp" />
-
+ android:text="@string/k2go_zc_sort_group" />
+
+
+