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" /> + + +