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/CloneFragment.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/CloneFragment.java index 6b4f94e0e..dbdc14ae6 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/CloneFragment.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/CloneFragment.java @@ -581,8 +581,8 @@ private void render() { updateBackGuard(); // ADFA-5151: keep the Back confinement in step with side + transfer state if (showcode != null) { showcode.setVisibility(View.GONE); codeblock.setVisibility(View.GONE); } if (stepTitle != null) { stepTitle.setVisibility(View.GONE); shareWifi.setVisibility(View.GONE); } - paintTab(tabSend, side == Side.SEND); - paintTab(tabReceive, side == Side.RECEIVE); + SegmentedTabs.paint(tabSend, side == Side.SEND); + SegmentedTabs.paint(tabReceive, side == Side.RECEIVE); if (atFork) { cloneHdr.setVisibility(View.VISIBLE); @@ -638,8 +638,8 @@ private void render() { // ADFA-5154: Send is two pages. Common chrome, then the page. actionFooter.setVisibility(View.GONE); // ADFA-5154: default hidden; only Copy's states re-show it netRow.setVisibility(View.VISIBLE); - paintTab(tabHotspot, mode == Mode.HOTSPOT); - paintTab(tabWifi, mode == Mode.WIFI); + SegmentedTabs.paint(tabHotspot, mode == Mode.HOTSPOT); + SegmentedTabs.paint(tabWifi, mode == Mode.WIFI); steps.setVisibility(View.VISIBLE); buildSteps(); advance.setVisibility(View.VISIBLE); @@ -1569,11 +1569,6 @@ private View arrow() { return a; } - private void paintTab(TextView t, boolean on) { - t.setBackgroundResource(on ? R.drawable.k2go_primary_bg : 0); - t.setTextColor(ContextCompat.getColor(requireContext(), on ? R.color.k2go_on_teal : R.color.k2go_muted)); - } - private void styleAdvance(boolean filled) { // ADFA-5346: shape/size come from the style; only toggle the fill + label emphasis by state. K2GoButtons.setFilledEmphasis(advance, filled); diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/ConnectFragment.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/ConnectFragment.java index d2f6a3e33..c377d11d2 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/ConnectFragment.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/ConnectFragment.java @@ -155,8 +155,8 @@ public void onDestroyView() { private void render() { if (!isAdded() || secSingle == null) return; - paintTab(tabHotspot, mode == Mode.HOTSPOT); - paintTab(tabWifi, mode == Mode.WIFI); + SegmentedTabs.paint(tabHotspot, mode == Mode.HOTSPOT); + SegmentedTabs.paint(tabWifi, mode == Mode.WIFI); finish.setVisibility(View.GONE); connFooter.setVisibility(View.VISIBLE); // default; the no-system state hides it advance.setVisibility(View.GONE); @@ -358,11 +358,6 @@ private View arrow() { return a; } - private void paintTab(TextView t, boolean on) { - t.setBackgroundResource(on ? R.drawable.k2go_primary_bg : 0); - t.setTextColor(ContextCompat.getColor(requireContext(), on ? R.color.k2go_on_teal : R.color.k2go_muted)); - } - private void styleAdvance(boolean filled) { // ADFA-5346: shape/size from the style; only toggle the fill + label emphasis by state. K2GoButtons.setFilledEmphasis(advance, filled); 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..50b560fb1 --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/K2GoFilterChip.java @@ -0,0 +1,101 @@ +/* + * ============================================================================ + * 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 android.view.ViewGroup; +import android.widget.HorizontalScrollView; + +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)); + } + + /** + * Wire a horizontally-scrollable filter-chip row so it never reads as "the last chip": a soft fade + * at the scrolled edges (right while there is more to the right, left once scrolled), and on + * (re)build a scroll that reveals the selected chip with its neighbours peeking instead of pinned + * flush to an edge. Call after (re)populating the row. K2GO-385 (design k2go-chip-scroll-affordance-v1). + */ + public static void revealSelected(HorizontalScrollView scroll, ViewGroup row) { + float d = scroll.getResources().getDisplayMetrics().density; + scroll.setHorizontalFadingEdgeEnabled(true); + // 34dp (a touch longer than the default) so the edge fade stays legible for low vision, not so + // faint it can be missed when a chip happens to end right at the row edge. + scroll.setFadingEdgeLength(Math.round(34 * d)); + scroll.post(() -> { + for (int i = 0; i < row.getChildCount(); i++) { + View c = row.getChildAt(i); + if (c instanceof Chip && ((Chip) c).isChecked()) { + int peek = c.getWidth(); // keep one chip of context so the active chip is not pinned + scroll.smoothScrollTo(Math.max(0, c.getLeft() - peek), 0); + return; + } + } + }); + } +} diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/SegmentedTabs.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/SegmentedTabs.java new file mode 100644 index 000000000..234993baf --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/SegmentedTabs.java @@ -0,0 +1,46 @@ +/* + * ============================================================================ + * Name : SegmentedTabs.java + * Author : AppDevForAll + * Copyright : Copyright (c) 2026 AppDevForAll + * Description : K2GO-385 (pill roles Q6, light variant). The Connect / Clone mode strips stay a + * two-segment teal strip (active = teal pill + on-teal text), but read as a + * single-select radio group to accessibility services: each tab is announced as a + * checkable RadioButton, checked when active, and marked selected. One definition + * instead of the identical paintTab that lived in both ConnectFragment and + * CloneFragment. Touch ripple comes from android:foreground on the tab in the layout. + * ============================================================================ + */ +package org.appdevforall.k2go.redesign; + +import android.view.View; +import android.widget.TextView; + +import androidx.core.content.ContextCompat; +import androidx.core.view.AccessibilityDelegateCompat; +import androidx.core.view.ViewCompat; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; + +import org.appdevforall.k2go.R; + +public final class SegmentedTabs { + + private SegmentedTabs() {} + + /** Paint one segment of a two-way mode strip and expose it as a checked/unchecked radio. */ + public static void paint(TextView tab, boolean checked) { + tab.setBackgroundResource(checked ? R.drawable.k2go_primary_bg : 0); + tab.setTextColor(ContextCompat.getColor(tab.getContext(), + checked ? R.color.k2go_on_teal : R.color.k2go_muted)); + tab.setSelected(checked); + ViewCompat.setAccessibilityDelegate(tab, new AccessibilityDelegateCompat() { + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { + super.onInitializeAccessibilityNodeInfo(host, info); + info.setClassName("android.widget.RadioButton"); + info.setCheckable(true); + info.setChecked(host.isSelected()); + } + }); + } +} diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/SettingsSubFragment.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/SettingsSubFragment.java index fd209d64f..f81b412d9 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/SettingsSubFragment.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/SettingsSubFragment.java @@ -402,7 +402,7 @@ private void buildAuthentication(Context ctx, LinearLayout list) { SettingsUi.caption(ctx, list, getString(R.string.k2go_auth_list_note)); } - /** A navigable row per service with a state chip (Default / Custom / Not installed). A service + /** A navigable row per service with a state badge (Default / Custom / Not installed). A service * that isn't reachable is dimmed but stays tappable so the sign-in can be pre-set. */ private void authServiceRow(Context ctx, LinearLayout list, String service, String name, String platform) { LinearLayout row = new LinearLayout(ctx); @@ -426,13 +426,12 @@ private void authServiceRow(Context ctx, LinearLayout list, String service, Stri com.google.android.material.R.style.TextAppearance_Material3_BodySmall, R.color.k2go_muted)); row.addView(col); - final TextView chip = new TextView(ctx); - chip.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_LabelSmall); - chip.setPadding(SettingsUi.dp(ctx, 10), SettingsUi.dp(ctx, 4), SettingsUi.dp(ctx, 10), SettingsUi.dp(ctx, 4)); - chip.setVisibility(View.GONE); - LinearLayout.LayoutParams chlp = new LinearLayout.LayoutParams(-2, -2); - chlp.rightMargin = SettingsUi.dp(ctx, 8); - row.addView(chip, chlp); + // K2GO-385 (pill roles Q4): the service state (Default / Custom / Not installed) is a STATUS, + // so it reads as a dot+text badge, not a filled pill. Colours unchanged -- Default/Not installed + // neutral, Custom teal. Built once, GONE until the async probe resolves. + final LinearLayout badge = K2GoStatusBadge.create(ctx, "", R.color.k2go_muted); + badge.setVisibility(View.GONE); + row.addView(badge); TextView chev = new TextView(ctx); chev.setText("›"); @@ -445,26 +444,24 @@ private void authServiceRow(Context ctx, LinearLayout list, String service, Stri if (!isAdded()) return; row.setAlpha(reachable ? 1f : 0.5f); if (!reachable) { - setChip(chip, getString(R.string.k2go_auth_chip_notinstalled), R.drawable.k2go_pill_bg, R.color.k2go_muted); + setBadge(badge, getString(R.string.k2go_auth_chip_notinstalled), R.color.k2go_muted); return; } CredentialsClient.describe(service, new CredentialsClient.DescribeCb() { @Override public void onOk(String user, String pass, boolean isDefault) { if (!isAdded()) return; - if (isDefault) setChip(chip, getString(R.string.k2go_auth_chip_default), R.drawable.k2go_pill_bg, R.color.k2go_muted); - else setChip(chip, getString(R.string.k2go_auth_chip_custom), R.drawable.k2go_pill_teal, R.color.k2go_teal); + if (isDefault) setBadge(badge, getString(R.string.k2go_auth_chip_default), R.color.k2go_muted); + else setBadge(badge, getString(R.string.k2go_auth_chip_custom), R.color.k2go_teal); } - @Override public void onErr() { /* leave the chip hidden on a load error */ } + @Override public void onErr() { /* leave the badge hidden on a load error */ } }); }); } - /** Style + reveal a state chip (Default / Custom / Not installed). */ - private void setChip(TextView chip, String text, int bgRes, int colorRes) { - chip.setText(text); - chip.setBackgroundResource(bgRes); - chip.setTextColor(ContextCompat.getColor(requireContext(), colorRes)); - chip.setVisibility(View.VISIBLE); + /** Style + reveal the service state badge (Default / Custom / Not installed). */ + private void setBadge(LinearLayout badge, String text, int colorRes) { + K2GoStatusBadge.style(badge, text, colorRes); + badge.setVisibility(View.VISIBLE); } private void buildServiceAuth(Context ctx, LinearLayout list, String service) { diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/Step1SystemFragment.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/Step1SystemFragment.java index 5dc991ccd..232f6556a 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/Step1SystemFragment.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/Step1SystemFragment.java @@ -25,10 +25,10 @@ public class Step1SystemFragment extends Fragment { private static final class Edition { final InstallationPlanner.Tier tier; final String name; - final String desc; final boolean recommended; + final String desc; ImageView radio; TextView sizeView; - Edition(InstallationPlanner.Tier t, String n, String d, boolean r) { - tier = t; name = n; desc = d; recommended = r; + Edition(InstallationPlanner.Tier t, String n, String d) { + tier = t; name = n; desc = d; } } @@ -56,14 +56,14 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c tint(barFree, R.color.k2go_hairline); editions.clear(); + // ADFA-5295: no "Recommended" edition -- Standard's size edge no longer buys a real UX + // advantage, so we don't steer users. K2GO-385: the dead badge scaffolding was removed. editions.add(new Edition(InstallationPlanner.Tier.BASIC, getString(R.string.k2go_edition_basic), - getString(R.string.k2go_edition_basic_desc), false)); - // ADFA-5295: no "Recommended" badge on Standard anymore — its size edge no longer buys a - // real design/UX advantage, so we don't steer users to it. + getString(R.string.k2go_edition_basic_desc))); editions.add(new Edition(InstallationPlanner.Tier.STANDARD, getString(R.string.k2go_edition_standard), - getString(R.string.k2go_edition_standard_desc), false)); + getString(R.string.k2go_edition_standard_desc))); editions.add(new Edition(InstallationPlanner.Tier.FULL, getString(R.string.k2go_edition_full), - getString(R.string.k2go_edition_full_desc), false)); + getString(R.string.k2go_edition_full_desc))); LinearLayout host = root.findViewById(R.id.k2go_editions); for (Edition e : editions) { @@ -72,8 +72,6 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c ((TextView) row.findViewById(R.id.k2go_edition_desc)).setText(e.desc); e.sizeView = row.findViewById(R.id.k2go_edition_size); e.sizeView.setText(sizeText(InstallationPlanner.fallbackOsSizeGb(e.tier))); // instant last-known - row.findViewById(R.id.k2go_edition_reco) - .setVisibility(e.recommended ? View.VISIBLE : View.GONE); e.radio = row.findViewById(R.id.k2go_edition_radio); row.setOnClickListener(v -> select(e.tier)); host.addView(row); 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..4005aba4f 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 @@ -18,6 +18,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; +import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; @@ -28,6 +29,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; @@ -46,6 +49,7 @@ public class ZimLandingFragment extends Fragment { private long freeMb = 0, totalMb = 0; private LinearLayout cats; private LinearLayout chipRow; + private HorizontalScrollView chipScroll; private TextView status, langLabel, langSub, storageLabel; private ProgressBar storageBar; private Button review; @@ -74,6 +78,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c cats = root.findViewById(R.id.k2go_zim_cats); chipRow = root.findViewById(R.id.k2go_zim_chips); + chipScroll = root.findViewById(R.id.k2go_zim_chips_scroll); status = root.findViewById(R.id.k2go_zim_status); langLabel = root.findViewById(R.id.k2go_zim_lang); langSub = root.findViewById(R.id.k2go_zim_lang_sub); @@ -249,40 +254,24 @@ private void buildChips() { chipRow.removeAllViews(); chipRow.addView(chip(getString(R.string.k2go_zim_grp_all), null)); for (KiwixGroups.Group g : KiwixGroups.ALL) chipRow.addView(chip(getString(g.chipLabel), g.key)); + K2GoFilterChip.revealSelected(chipScroll, chipRow); } 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/bg_beta_pill.xml b/controller/app/src/main/res/drawable/bg_beta_pill.xml deleted file mode 100644 index 37005ecf0..000000000 --- a/controller/app/src/main/res/drawable/bg_beta_pill.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - 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/drawable/ic_share.xml b/controller/app/src/main/res/drawable/ic_share.xml new file mode 100644 index 000000000..6eb493c0c --- /dev/null +++ b/controller/app/src/main/res/drawable/ic_share.xml @@ -0,0 +1,8 @@ + + + + diff --git a/controller/app/src/main/res/layout/activity_k2go_setup_progress.xml b/controller/app/src/main/res/layout/activity_k2go_setup_progress.xml index 6b3414937..daea558be 100644 --- a/controller/app/src/main/res/layout/activity_k2go_setup_progress.xml +++ b/controller/app/src/main/res/layout/activity_k2go_setup_progress.xml @@ -93,9 +93,6 @@ android:layout_height="52dp" android:layout_marginTop="16dp" android:text="@string/k2go_zim_finish" - android:textColor="@android:color/white" - android:textAllCaps="false" - android:backgroundTint="@color/k2go_teal" android:visibility="gone" /> + android:text="@string/k2go_setup_back" />