diff --git a/app/src/main/java/cn/lineai/ui/component/ActionRowView.java b/app/src/main/java/cn/lineai/ui/component/ActionRowView.java index d804ad43..1abdcdbe 100644 --- a/app/src/main/java/cn/lineai/ui/component/ActionRowView.java +++ b/app/src/main/java/cn/lineai/ui/component/ActionRowView.java @@ -14,17 +14,18 @@ public ActionRowView(Context context, int iconType, String label, String desc, b super(context); setOrientation(HORIZONTAL); setGravity(Gravity.CENTER_VERTICAL); - setMinimumHeight(LineTheme.dp(context, 56)); + setMinimumHeight(LineTheme.dp(context, 68)); LineTheme.padding(this, LineTheme.LG, LineTheme.MD, LineTheme.LG, LineTheme.MD); FrameLayout iconWrap = new FrameLayout(context); + iconWrap.setBackground(LineTheme.rounded(context, destructive ? LineTheme.DANGER_MUTED : LineTheme.ACCENT_MUTED, 8)); IconButtonView icon = new IconButtonView(context, iconType); - icon.setIconColor(destructive ? LineTheme.DANGER : LineTheme.TEXT_SECONDARY); - icon.setIconSizeDp(24, 20); + icon.setIconColor(destructive ? LineTheme.DANGER : LineTheme.ACCENT); + icon.setIconSizeDp(36, 20); icon.setClickable(false); - iconWrap.addView(icon, new FrameLayout.LayoutParams(LineTheme.dp(context, 24), LineTheme.dp(context, 24), Gravity.CENTER)); - addView(iconWrap, new LayoutParams(LineTheme.dp(context, 24), LineTheme.dp(context, 24))); + iconWrap.addView(icon, new FrameLayout.LayoutParams(LineTheme.dp(context, 36), LineTheme.dp(context, 36), Gravity.CENTER)); + addView(iconWrap, new LayoutParams(LineTheme.dp(context, 36), LineTheme.dp(context, 36))); LinearLayout textWrap = new LinearLayout(context); textWrap.setOrientation(VERTICAL); @@ -37,10 +38,10 @@ public ActionRowView(Context context, int iconType, String label, String desc, b textWrap.addView(title, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); if (desc != null && desc.length() > 0) { - TextView description = LineTheme.text(context, desc, 14, LineTheme.TEXT_TERTIARY, Typeface.NORMAL); + TextView description = LineTheme.text(context, desc, LineTheme.FONT_XS, LineTheme.TEXT_TERTIARY, Typeface.NORMAL); description.setLineSpacing(LineTheme.dp(context, 3), 1f); LinearLayout.LayoutParams descParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - descParams.topMargin = LineTheme.dp(context, 6); + descParams.topMargin = LineTheme.dp(context, 2); textWrap.addView(description, descParams); } @@ -54,7 +55,6 @@ public ActionRowView(Context context, int iconType, String label, String desc, b if (onClick != null) { setClickable(true); - setBackground(LineTheme.pressable(context)); setOnClickListener(v -> onClick.run()); } } diff --git a/app/src/main/java/cn/lineai/ui/component/CardViewHelper.java b/app/src/main/java/cn/lineai/ui/component/CardViewHelper.java index 96336551..7b572f48 100644 --- a/app/src/main/java/cn/lineai/ui/component/CardViewHelper.java +++ b/app/src/main/java/cn/lineai/ui/component/CardViewHelper.java @@ -1,12 +1,76 @@ package cn.lineai.ui.component; +import cn.lineai.ui.theme.IconButtonView; +import cn.lineai.ui.theme.LineTheme; + +import android.content.Context; +import android.graphics.Typeface; +import android.view.Gravity; +import android.widget.FrameLayout; import android.widget.LinearLayout; +import android.widget.TextView; + public final class CardViewHelper { - public interface OnCardClickListener { void onCardClick(String id); } - private CardViewHelper() { } - public static void addCard(LinearLayout content, String id, String title, String desc, String badge, int icon, OnCardClickListener listener) { - ActionRowView row = new ActionRowView(content.getContext(), icon, title, desc, false, true, () -> listener.onCardClick(id)); - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(-1, -2); - params.bottomMargin = cn.lineai.ui.theme.LineTheme.dp(content.getContext(), 12); - content.addView(row, params); + + public interface OnCardClickListener { + void onCardClick(String id); + } + + private CardViewHelper() { + } + + public static void addCard(LinearLayout content, String id, String title, String desc, String badge, int iconType, OnCardClickListener clickListener) { + Context context = content.getContext(); + LinearLayout card = new LinearLayout(context); + card.setOrientation(LinearLayout.HORIZONTAL); + card.setGravity(Gravity.CENTER_VERTICAL); + card.setClickable(true); + card.setOnClickListener(v -> clickListener.onCardClick(id)); + card.setBackground(LineTheme.roundedStroke(context, LineTheme.SURFACE_ELEVATED, 12, LineTheme.BORDER)); + LineTheme.padding(card, LineTheme.LG, LineTheme.MD, LineTheme.MD, LineTheme.MD); + LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + cardParams.bottomMargin = LineTheme.dp(context, LineTheme.SM); + content.addView(card, cardParams); + + FrameLayout iconWrap = new FrameLayout(context); + iconWrap.setBackground(LineTheme.rounded(context, LineTheme.ACCENT_MUTED, 12)); + IconButtonView icon = new IconButtonView(context, iconType); + icon.setIconColor(LineTheme.ACCENT); + icon.setIconSizeDp(44, 22); + icon.setClickable(false); + iconWrap.addView(icon, new FrameLayout.LayoutParams(LineTheme.dp(context, 44), LineTheme.dp(context, 44), Gravity.CENTER)); + card.addView(iconWrap, new LinearLayout.LayoutParams(LineTheme.dp(context, 44), LineTheme.dp(context, 44))); + + LinearLayout text = new LinearLayout(context); + text.setOrientation(LinearLayout.VERTICAL); + LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f); + textParams.leftMargin = LineTheme.dp(context, LineTheme.MD); + textParams.rightMargin = LineTheme.dp(context, LineTheme.MD); + card.addView(text, textParams); + + LinearLayout titleRow = new LinearLayout(context); + titleRow.setOrientation(LinearLayout.HORIZONTAL); + titleRow.setGravity(Gravity.CENTER_VERTICAL); + text.addView(titleRow, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); + + TextView titleView = LineTheme.text(context, title, LineTheme.FONT_LG, LineTheme.TEXT, Typeface.BOLD); + titleRow.addView(titleView, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); + TextView badgeView = LineTheme.text(context, badge, LineTheme.FONT_XS, LineTheme.ACCENT, Typeface.BOLD); + badgeView.setBackground(LineTheme.rounded(context, LineTheme.ACCENT_MUTED, 999)); + LineTheme.padding(badgeView, LineTheme.SM, 3, LineTheme.SM, 3); + LinearLayout.LayoutParams badgeParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + badgeParams.leftMargin = LineTheme.dp(context, LineTheme.SM); + titleRow.addView(badgeView, badgeParams); + + TextView descView = LineTheme.text(context, desc, LineTheme.FONT_SM, LineTheme.TEXT_TERTIARY, Typeface.NORMAL); + descView.setLineSpacing(LineTheme.dp(context, 3), 1f); + LinearLayout.LayoutParams descParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + descParams.topMargin = LineTheme.dp(context, LineTheme.XS); + text.addView(descView, descParams); + + IconButtonView chevron = new IconButtonView(context, IconButtonView.CHEVRON_RIGHT); + chevron.setIconColor(LineTheme.TEXT_TERTIARY); + chevron.setIconSizeDp(20, 17); + chevron.setClickable(false); + card.addView(chevron, new LinearLayout.LayoutParams(LineTheme.dp(context, 20), LineTheme.dp(context, 20))); } } diff --git a/app/src/main/java/cn/lineai/ui/component/MCPSettingsScreenView.java b/app/src/main/java/cn/lineai/ui/component/MCPSettingsScreenView.java index eca65ee9..938be0fc 100644 --- a/app/src/main/java/cn/lineai/ui/component/MCPSettingsScreenView.java +++ b/app/src/main/java/cn/lineai/ui/component/MCPSettingsScreenView.java @@ -41,7 +41,7 @@ public MCPSettingsScreenView(Context context, McpSettingsState state, Listener l this.listener = listener; this.state = state == null ? new McpSettingsState(EXECUTION_LOCAL, null) : state; LinearLayout content = getContent(); - LineTheme.padding(content, 16, 8, 16, 48); + LineTheme.padding(content, LineTheme.LG, LineTheme.LG, LineTheme.LG, 100); addExecutionTarget(content); if (EXECUTION_SSH.equals(this.state.getExecutionMode())) { @@ -54,13 +54,16 @@ private void addExecutionTarget(LinearLayout content) { Context context = content.getContext(); LinearLayout card = card(context); card.addView(title(context, context.getString(R.string.screen_mcp_section_execution))); - for (String mode : new String[] {EXECUTION_LOCAL, EXECUTION_SSH, EXECUTION_TERMINAL_PROVIDER}) { - int label = EXECUTION_LOCAL.equals(mode) ? R.string.screen_mcp_execution_local : EXECUTION_SSH.equals(mode) ? R.string.screen_mcp_execution_ssh : R.string.screen_mcp_execution_terminal_provider; - LayoutParams modeParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - modeParams.topMargin = LineTheme.dp(context, 8); - card.addView(new OptionRowView(context, IconButtonView.TERMINAL, context.getString(label), null, - mode.equals(state.getExecutionMode()), () -> listener.onExecutionModeChanged(mode)), modeParams); - } + LinearLayout segment = new LinearLayout(context); + segment.setOrientation(HORIZONTAL); + segment.setBackground(LineTheme.rounded(context, LineTheme.SURFACE_LIGHT, 8)); + LineTheme.padding(segment, 3, 3, 3, 3); + addModeButton(segment, context.getString(R.string.screen_mcp_execution_local), EXECUTION_LOCAL); + addModeButton(segment, context.getString(R.string.screen_mcp_execution_ssh), EXECUTION_SSH); + addModeButton(segment, context.getString(R.string.screen_mcp_execution_terminal_provider), EXECUTION_TERMINAL_PROVIDER); + LinearLayout.LayoutParams segmentParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LineTheme.dp(context, 42)); + segmentParams.topMargin = LineTheme.dp(context, LineTheme.SM); + card.addView(segment, segmentParams); String executionDesc; if (EXECUTION_LOCAL.equals(state.getExecutionMode())) { executionDesc = context.getString(R.string.screen_mcp_execution_local_desc); @@ -129,6 +132,7 @@ private void addToolCard(LinearLayout content, int iconType, McpToolConfig confi icon.setIconColor(config.isEnabled() ? LineTheme.ACCENT : LineTheme.TEXT_TERTIARY); icon.setIconSizeDp(36, 18); icon.setClickable(false); + icon.setBackground(LineTheme.rounded(context, LineTheme.ACCENT_MUTED, 18)); header.addView(icon, new LinearLayout.LayoutParams(LineTheme.dp(context, 36), LineTheme.dp(context, 36))); @@ -177,11 +181,8 @@ private void tintSwitch(Switch toggle, CompoundButton.OnCheckedChangeListener li private LinearLayout card(Context context) { LinearLayout card = new LinearLayout(context); card.setOrientation(LinearLayout.VERTICAL); - card.setBackground(LineTheme.roundedStroke(context, LineTheme.SURFACE_ELEVATED, 14, LineTheme.BORDER_LIGHT)); - LineTheme.padding(card, 16, 14, 16, 16); - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - params.bottomMargin = LineTheme.dp(context, 14); - card.setLayoutParams(params); + card.setBackground(LineTheme.rounded(context, LineTheme.SURFACE_ELEVATED, 12)); + LineTheme.padding(card, LineTheme.LG, LineTheme.LG, LineTheme.LG, LineTheme.LG); return card; } diff --git a/app/src/main/java/cn/lineai/ui/component/MemorySettingsScreenView.java b/app/src/main/java/cn/lineai/ui/component/MemorySettingsScreenView.java index 6407ddda..87b2e78d 100644 --- a/app/src/main/java/cn/lineai/ui/component/MemorySettingsScreenView.java +++ b/app/src/main/java/cn/lineai/ui/component/MemorySettingsScreenView.java @@ -11,7 +11,6 @@ import android.view.Gravity; import android.view.View; import android.view.Window; -import android.view.WindowManager; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.LinearLayout; @@ -30,7 +29,7 @@ import java.util.Locale; import java.util.Set; -public final class MemorySettingsScreenView extends ScreenSurfaceView { +public final class MemorySettingsScreenView extends LinearLayout { public interface Listener { void onBack(); @@ -385,7 +384,7 @@ private Dialog createDialog() { private LinearLayout dialogPanel(Context context) { LinearLayout panel = new LinearLayout(context); panel.setOrientation(LinearLayout.VERTICAL); - panel.setBackground(LineTheme.rounded(context, LineTheme.BG, 24)); + panel.setBackground(LineTheme.rounded(context, LineTheme.SURFACE_ELEVATED, 12)); LineTheme.padding(panel, LineTheme.LG, LineTheme.LG, LineTheme.LG, LineTheme.LG); return panel; } diff --git a/app/src/main/java/cn/lineai/ui/component/ModelAddOptionsScreenView.java b/app/src/main/java/cn/lineai/ui/component/ModelAddOptionsScreenView.java index e4ea2554..71c417c7 100644 --- a/app/src/main/java/cn/lineai/ui/component/ModelAddOptionsScreenView.java +++ b/app/src/main/java/cn/lineai/ui/component/ModelAddOptionsScreenView.java @@ -27,7 +27,7 @@ public interface Listener { public ModelAddOptionsScreenView(Context context, Listener listener) { super(context, context.getString(R.string.screen_model_add_options_title), listener::onBack, null); LinearLayout content = getContent(); - LineTheme.padding(content, 16, 8, 16, 48); + LineTheme.padding(content, LineTheme.LG, LineTheme.LG, LineTheme.LG, LineTheme.LG); addLargeCard(content, IconButtonView.SLIDERS_HORIZONTAL, context.getString(R.string.screen_model_add_options_custom), context.getString(R.string.screen_model_add_options_custom_desc), listener::onCustom); @@ -57,19 +57,89 @@ public ModelAddOptionsScreenView(Context context, Listener listener) { } private void addLargeCard(LinearLayout content, int iconType, String title, String desc, Runnable onClick) { - content.addView(new ActionRowView(getContext(), iconType, title, desc, false, true, onClick), cardParams()); + Context context = content.getContext(); + LinearLayout card = new LinearLayout(context); + card.setOrientation(HORIZONTAL); + card.setGravity(Gravity.CENTER_VERTICAL); + card.setMinimumHeight(LineTheme.dp(context, 92)); + card.setBackground(LineTheme.roundedStroke(context, LineTheme.SURFACE_ELEVATED, 12, LineTheme.BORDER_LIGHT)); + card.setClickable(true); + card.setOnClickListener(v -> onClick.run()); + LineTheme.padding(card, LineTheme.LG, LineTheme.LG, LineTheme.LG, LineTheme.LG); + + FrameLayout largeIcon = new FrameLayout(context); + largeIcon.setBackground(LineTheme.rounded(context, LineTheme.ACCENT_MUTED, 8)); + IconButtonView icon = new IconButtonView(context, iconType); + icon.setIconColor(LineTheme.ACCENT); + icon.setIconSizeDp(44, 22); + icon.setClickable(false); + largeIcon.addView(icon, new FrameLayout.LayoutParams(LineTheme.dp(context, 44), LineTheme.dp(context, 44), Gravity.CENTER)); + card.addView(largeIcon, new LinearLayout.LayoutParams(LineTheme.dp(context, 44), LineTheme.dp(context, 44))); + + LinearLayout text = new LinearLayout(context); + text.setOrientation(VERTICAL); + LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f); + textParams.leftMargin = LineTheme.dp(context, LineTheme.MD); + textParams.rightMargin = LineTheme.dp(context, LineTheme.MD); + card.addView(text, textParams); + text.addView(LineTheme.text(context, title, LineTheme.FONT_MD, LineTheme.TEXT, Typeface.BOLD), new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + TextView descView = LineTheme.text(context, desc, LineTheme.FONT_XS, LineTheme.TEXT_TERTIARY, Typeface.NORMAL); + descView.setLineSpacing(LineTheme.dp(context, 3), 1f); + LinearLayout.LayoutParams descParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + descParams.topMargin = LineTheme.dp(context, LineTheme.XS); + text.addView(descView, descParams); + + IconButtonView chevron = new IconButtonView(context, IconButtonView.CHEVRON_RIGHT); + chevron.setIconColor(LineTheme.TEXT_TERTIARY); + chevron.setIconSizeDp(19, 17); + chevron.setClickable(false); + card.addView(chevron, new LinearLayout.LayoutParams(LineTheme.dp(context, 19), LineTheme.dp(context, 19))); + + LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + cardParams.bottomMargin = LineTheme.dp(context, LineTheme.SM); + content.addView(card, cardParams); } + private void addProvider(LinearLayout content, ModelProviderPreset preset, Listener listener) { - content.addView(new ActionRowView(getContext(), IconButtonView.BOX, - ModelProviderPresetStrings.getLabel(getContext(), preset.getId()), - ModelProviderPresetStrings.getDesc(getContext(), preset.getId()), false, true, - () -> listener.onProvider(preset.getId())), cardParams()); - } + Context context = content.getContext(); + String name = ModelProviderPresetStrings.getLabel(context, preset.getId()); + LinearLayout row = new LinearLayout(context); + row.setOrientation(HORIZONTAL); + row.setGravity(Gravity.CENTER_VERTICAL); + row.setBackground(LineTheme.roundedStroke(context, LineTheme.SURFACE_ELEVATED, 12, LineTheme.BORDER_LIGHT)); + row.setClickable(true); + row.setOnClickListener(v -> listener.onProvider(preset.getId())); + LineTheme.padding(row, LineTheme.MD, LineTheme.MD, LineTheme.MD, LineTheme.MD); + + TextView initial = LineTheme.text(context, name.substring(0, 1), LineTheme.FONT_MD, LineTheme.ACCENT, Typeface.BOLD); + initial.setGravity(Gravity.CENTER); + initial.setBackground(LineTheme.rounded(context, LineTheme.ACCENT_MUTED, 8)); + row.addView(initial, new LinearLayout.LayoutParams(LineTheme.dp(context, 38), LineTheme.dp(context, 38))); + + LinearLayout text = new LinearLayout(context); + text.setOrientation(VERTICAL); + LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f); + textParams.leftMargin = LineTheme.dp(context, LineTheme.MD); + textParams.rightMargin = LineTheme.dp(context, LineTheme.MD); + row.addView(text, textParams); + TextView title = LineTheme.text(context, name, LineTheme.FONT_MD, LineTheme.TEXT, Typeface.BOLD); + title.setSingleLine(true); + text.addView(title, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + TextView sub = LineTheme.text(context, ModelProviderPresetStrings.getDesc(context, preset.getId()) + " · " + protocolLabel(preset), LineTheme.FONT_XS, LineTheme.TEXT_TERTIARY, Typeface.NORMAL); + sub.setSingleLine(true); + LinearLayout.LayoutParams subParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + subParams.topMargin = LineTheme.dp(context, 3); + text.addView(sub, subParams); + + IconButtonView chevron = new IconButtonView(context, IconButtonView.CHEVRON_RIGHT); + chevron.setIconColor(LineTheme.TEXT_TERTIARY); + chevron.setIconSizeDp(18, 16); + chevron.setClickable(false); + row.addView(chevron, new LinearLayout.LayoutParams(LineTheme.dp(context, 18), LineTheme.dp(context, 18))); - private LayoutParams cardParams() { - LayoutParams params = new LayoutParams(-1, -2); - params.bottomMargin = LineTheme.dp(getContext(), 8); - return params; + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + params.bottomMargin = LineTheme.dp(context, LineTheme.SM); + content.addView(row, params); } private String protocolLabel(ModelProviderPreset preset) { diff --git a/app/src/main/java/cn/lineai/ui/component/OptionRowView.java b/app/src/main/java/cn/lineai/ui/component/OptionRowView.java index 6d0704bb..8f7589dc 100644 --- a/app/src/main/java/cn/lineai/ui/component/OptionRowView.java +++ b/app/src/main/java/cn/lineai/ui/component/OptionRowView.java @@ -36,9 +36,9 @@ public OptionRowView(Context context, int iconType, String label, String desc, b content.addView(labelView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); if (desc != null && desc.length() > 0) { - TextView descView = LineTheme.text(context, desc, 14, LineTheme.TEXT_TERTIARY, Typeface.NORMAL); + TextView descView = LineTheme.text(context, desc, LineTheme.FONT_XS, LineTheme.TEXT_TERTIARY, Typeface.NORMAL); LinearLayout.LayoutParams descParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - descParams.topMargin = LineTheme.dp(context, 6); + descParams.topMargin = LineTheme.dp(context, 2); content.addView(descView, descParams); } @@ -51,10 +51,10 @@ public OptionRowView(Context context, int iconType, String label, String desc, b public void setActive(boolean active) { this.active = active; - setBackground(active ? LineTheme.roundedStroke(getContext(), LineTheme.INPUT_BG, 12, LineTheme.ACCENT) : LineTheme.pressable(getContext())); + setBackgroundColor(active ? LineTheme.ACCENT_MUTED : android.graphics.Color.TRANSPARENT); setSelected(active); - icon.setIconColor(LineTheme.TEXT_SECONDARY); - labelView.setTextColor(LineTheme.TEXT); + icon.setIconColor(active ? LineTheme.ACCENT : LineTheme.TEXT_SECONDARY); + labelView.setTextColor(active ? LineTheme.ACCENT : LineTheme.TEXT); labelView.setTypeface(Typeface.create(active ? "sans-serif-medium" : "sans-serif", Typeface.NORMAL)); } diff --git a/app/src/main/java/cn/lineai/ui/component/SectionHeaderView.java b/app/src/main/java/cn/lineai/ui/component/SectionHeaderView.java index 33df16be..e7b2ff1b 100644 --- a/app/src/main/java/cn/lineai/ui/component/SectionHeaderView.java +++ b/app/src/main/java/cn/lineai/ui/component/SectionHeaderView.java @@ -5,12 +5,12 @@ public final class SectionHeaderView extends TextView { public SectionHeaderView(Context context, String title) { super(context); - setText(title == null ? "" : title); - setTextColor(LineTheme.TEXT_SECONDARY); - setTextSize(14); + setText(title == null ? "" : title.toUpperCase(java.util.Locale.ROOT)); + setTextColor(LineTheme.TEXT_TERTIARY); + setTextSize(LineTheme.FONT_XS); setIncludeFontPadding(false); setTypeface(android.graphics.Typeface.create("sans-serif-medium", android.graphics.Typeface.NORMAL)); - LineTheme.padding(this, 28, 0, 28, 0); - if (android.os.Build.VERSION.SDK_INT >= 28) setAccessibilityHeading(true); + setLetterSpacing(0.05f); + LineTheme.padding(this, LineTheme.LG, 0, LineTheme.LG, 0); } } diff --git a/app/src/main/java/cn/lineai/ui/component/SettingsScreenView.java b/app/src/main/java/cn/lineai/ui/component/SettingsScreenView.java index 12b843eb..df777495 100644 --- a/app/src/main/java/cn/lineai/ui/component/SettingsScreenView.java +++ b/app/src/main/java/cn/lineai/ui/component/SettingsScreenView.java @@ -12,7 +12,7 @@ import android.widget.TextView; import cn.lineai.R; -public final class SettingsScreenView extends ScreenSurfaceView { +public final class SettingsScreenView extends LinearLayout { public interface Listener { void onBack(); @@ -33,16 +33,16 @@ public SettingsScreenView(Context context, Listener listener) { scrollView.setFillViewport(false); LinearLayout content = new LinearLayout(context); content.setOrientation(VERTICAL); - LineTheme.padding(content, 0, 0, 0, 48); + LineTheme.padding(content, 0, 0, 0, 100); scrollView.addView(content, new ScrollView.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); addView(scrollView, new LayoutParams(LayoutParams.MATCH_PARENT, 0, 1f)); content.addView(new ActionRowView(context, IconButtonView.SPARKLES, context.getString(R.string.settings_row_tutorial_title), - null, + context.getString(R.string.settings_row_tutorial_desc), false, true, () -> listener.onItem("tutorialFromSettings")), - tutorialParams(context)); + new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); addSection(content, context.getString(R.string.screen_settings_section_ai), new RowSpec[] { new RowSpec("models", context.getString(R.string.settings_row_models_title), context.getString(R.string.settings_row_models_desc), IconButtonView.BOX), @@ -74,16 +74,80 @@ public SettingsScreenView(Context context, Listener listener) { }); } - private LayoutParams tutorialParams(Context context) { - LayoutParams p = new LayoutParams(-1, -2);p.leftMargin=p.rightMargin=LineTheme.dp(context,12);return p; - } private void addSection(LinearLayout content, String title, RowSpec[] rows) { - SettingsSectionView section = new SettingsSectionView(getContext(), title); - for (RowSpec row : rows) { - section.addRow(new ActionRowView(getContext(), row.icon, row.label, null, - false, true, () -> listener.onItem(row.id)), false); + Context context = getContext(); + TextView sectionTitle = LineTheme.textMedium(context, title.toUpperCase(java.util.Locale.ROOT), LineTheme.FONT_XS, LineTheme.TEXT_TERTIARY); + sectionTitle.setLetterSpacing(0.05f); + LineTheme.padding(sectionTitle, LineTheme.LG, 0, LineTheme.LG, 0); + LinearLayout.LayoutParams sectionParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + sectionParams.topMargin = LineTheme.dp(context, LineTheme.XL); + sectionParams.bottomMargin = LineTheme.dp(context, LineTheme.MD); + content.addView(sectionTitle, sectionParams); + + LinearLayout group = new LinearLayout(context); + group.setOrientation(VERTICAL); + group.setBackground(LineTheme.rounded(context, LineTheme.SURFACE_ELEVATED, 12)); + LinearLayout.LayoutParams groupParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + groupParams.leftMargin = LineTheme.dp(context, LineTheme.LG); + groupParams.rightMargin = LineTheme.dp(context, LineTheme.LG); + content.addView(group, groupParams); + + for (int i = 0; i < rows.length; i++) { + group.addView(rowView(rows[i], i < rows.length - 1), new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + } + } + + private View rowView(RowSpec row, boolean divider) { + Context context = getContext(); + LinearLayout item = new LinearLayout(context); + item.setOrientation(HORIZONTAL); + item.setGravity(Gravity.CENTER_VERTICAL); + item.setClickable(true); + LineTheme.padding(item, LineTheme.LG, LineTheme.MD, LineTheme.LG, LineTheme.MD); + + FrameLayout iconWrap = new FrameLayout(context); + iconWrap.setBackground(LineTheme.rounded(context, LineTheme.ACCENT_MUTED, 18)); + IconButtonView icon = new IconButtonView(context, row.icon); + icon.setIconColor(LineTheme.ACCENT); + icon.setIconSizeDp(36, 20); + icon.setClickable(false); + iconWrap.addView(icon, new FrameLayout.LayoutParams(LineTheme.dp(context, 36), LineTheme.dp(context, 36), Gravity.CENTER)); + item.addView(iconWrap, new LayoutParams(LineTheme.dp(context, 36), LineTheme.dp(context, 36))); + + LinearLayout labels = new LinearLayout(context); + labels.setOrientation(VERTICAL); + LinearLayout.LayoutParams labelParams = new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f); + labelParams.leftMargin = LineTheme.dp(context, LineTheme.MD); + labelParams.rightMargin = LineTheme.dp(context, LineTheme.MD); + item.addView(labels, labelParams); + + TextView label = LineTheme.textMedium(context, row.label, LineTheme.FONT_MD, LineTheme.TEXT); + labels.addView(label, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + TextView desc = LineTheme.text(context, row.desc, LineTheme.FONT_XS, LineTheme.TEXT_TERTIARY, Typeface.NORMAL); + LinearLayout.LayoutParams descParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + descParams.topMargin = LineTheme.dp(context, 2); + labels.addView(desc, descParams); + + item.setOnClickListener(v -> listener.onItem(row.id)); + IconButtonView chevron = new IconButtonView(context, IconButtonView.CHEVRON_RIGHT); + chevron.setIconColor(LineTheme.TEXT_TERTIARY); + chevron.setIconSizeDp(20, 16); + chevron.setClickable(false); + item.addView(chevron, new LayoutParams(LineTheme.dp(context, 20), LineTheme.dp(context, 20))); + + if (!divider) { + return item; } - content.addView(section, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + + LinearLayout wrapper = new LinearLayout(context); + wrapper.setOrientation(VERTICAL); + wrapper.addView(item, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + View line = new View(context); + line.setBackgroundColor(LineTheme.BORDER_LIGHT); + LinearLayout.LayoutParams lineParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, 1); + lineParams.leftMargin = LineTheme.dp(context, 68); + wrapper.addView(line, lineParams); + return wrapper; } private static final class RowSpec { diff --git a/app/src/main/java/cn/lineai/ui/component/SettingsSectionView.java b/app/src/main/java/cn/lineai/ui/component/SettingsSectionView.java index 2e4a4d3a..bee9098b 100644 --- a/app/src/main/java/cn/lineai/ui/component/SettingsSectionView.java +++ b/app/src/main/java/cn/lineai/ui/component/SettingsSectionView.java @@ -1,32 +1,66 @@ package cn.lineai.ui.component; import android.content.Context; import android.view.View; -import android.view.ViewGroup; import android.widget.LinearLayout; import cn.lineai.ui.theme.LineTheme; public final class SettingsSectionView extends LinearLayout { private final SectionHeaderView header; private final LinearLayout group; public SettingsSectionView(Context context, String title) { - super(context); setOrientation(VERTICAL); + super(context); + setOrientation(VERTICAL); + header = new SectionHeaderView(context, title); - LayoutParams hp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - hp.topMargin = LineTheme.dp(context, 28); hp.bottomMargin = LineTheme.dp(context, 8); - addView(header, hp); - group = new LinearLayout(context); group.setOrientation(VERTICAL); - LayoutParams gp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - gp.leftMargin = gp.rightMargin = LineTheme.dp(context, 16); - addView(group, gp); + LinearLayout.LayoutParams headerParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + headerParams.topMargin = LineTheme.dp(context, LineTheme.XL); + headerParams.bottomMargin = LineTheme.dp(context, LineTheme.MD); + addView(header, headerParams); + + group = new LinearLayout(context); + group.setOrientation(VERTICAL); + group.setBackground(LineTheme.rounded(context, LineTheme.SURFACE_ELEVATED, 12)); + LinearLayout.LayoutParams groupParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + groupParams.leftMargin = LineTheme.dp(context, LineTheme.LG); + groupParams.rightMargin = LineTheme.dp(context, LineTheme.LG); + addView(group, groupParams); } - public void addRow(View row, boolean divider) { addRow(row, divider, 0); } - public void addRow(View row, boolean divider, int inset) { - if (row == null) return; - if (row.getParent() instanceof ViewGroup) ((ViewGroup)row.getParent()).removeView(row); - LayoutParams rowParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - rowParams.topMargin = group.getChildCount() == 0 ? 0 : LineTheme.dp(getContext(), 8); - group.addView(row, rowParams); + + public void addRow(View row, boolean divider) { + addRow(row, divider, 0); + } + + public void addRow(View row, boolean divider, int dividerInsetDp) { + if (row == null) { + return; + } + if (row.getParent() instanceof android.view.ViewGroup) { + ((android.view.ViewGroup) row.getParent()).removeView(row); + } + if (!divider) { + group.addView(row, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + return; + } + + LinearLayout wrapper = new LinearLayout(getContext()); + wrapper.setOrientation(VERTICAL); + wrapper.addView(row, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + View line = new View(getContext()); + line.setBackgroundColor(LineTheme.BORDER_LIGHT); + LinearLayout.LayoutParams lineParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, 1); + lineParams.leftMargin = LineTheme.dp(getContext(), dividerInsetDp); + wrapper.addView(line, lineParams); + group.addView(wrapper, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); + } + + public LinearLayout getGroup() { + return group; + } + + public void setTitle(String title) { + header.setText((title == null ? "" : title).toUpperCase(java.util.Locale.ROOT)); + } + + public void removeAllRows() { + group.removeAllViews(); } - public LinearLayout getGroup() { return group; } - public void setTitle(String title) { header.setText(title == null ? "" : title); } - public void removeAllRows() { group.removeAllViews(); } } diff --git a/app/src/main/java/cn/lineai/ui/component/SimpleSettingsScreenView.java b/app/src/main/java/cn/lineai/ui/component/SimpleSettingsScreenView.java index 4d221ce0..a9b941f0 100644 --- a/app/src/main/java/cn/lineai/ui/component/SimpleSettingsScreenView.java +++ b/app/src/main/java/cn/lineai/ui/component/SimpleSettingsScreenView.java @@ -9,7 +9,7 @@ import android.widget.ScrollView; import android.widget.TextView; -public final class SimpleSettingsScreenView extends ScreenSurfaceView { +public final class SimpleSettingsScreenView extends LinearLayout { public interface Listener { void onBack(); } @@ -37,7 +37,7 @@ public SimpleSettingsScreenView(Context context, String title, String subtitle, LinearLayout group = new LinearLayout(context); group.setOrientation(VERTICAL); - group.setBackground(null); + group.setBackground(LineTheme.rounded(context, LineTheme.SURFACE_ELEVATED, 12)); content.addView(group, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); for (int i = 0; i < rows.length; i++) { @@ -55,6 +55,9 @@ private View row(Context context, String text, boolean divider) { LineTheme.padding(row, LineTheme.LG, LineTheme.MD, LineTheme.LG, LineTheme.MD); TextView label = LineTheme.textMedium(context, text, LineTheme.FONT_MD, LineTheme.TEXT); row.addView(label, new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f)); + View dot = new View(context); + dot.setBackground(LineTheme.rounded(context, LineTheme.ACCENT, 4)); + row.addView(dot, new LinearLayout.LayoutParams(LineTheme.dp(context, 8), LineTheme.dp(context, 8))); wrapper.addView(row, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); if (divider) { diff --git a/app/src/main/java/cn/lineai/ui/component/SshSettingsScreenView.java b/app/src/main/java/cn/lineai/ui/component/SshSettingsScreenView.java index 1b2b719b..254fe1f0 100644 --- a/app/src/main/java/cn/lineai/ui/component/SshSettingsScreenView.java +++ b/app/src/main/java/cn/lineai/ui/component/SshSettingsScreenView.java @@ -39,7 +39,7 @@ public SshSettingsScreenView(Context context, Listener listener) { SshConfig config = listener.onLoadConfig(); LinearLayout content = getContent(); - LineTheme.padding(content, 28, 8, 28, 48); + LineTheme.padding(content, LineTheme.LG, LineTheme.LG, LineTheme.LG, 100); LinearLayout intro = card(context); intro.addView(title(context, context.getString(R.string.screen_ssh_section_server))); @@ -197,8 +197,8 @@ private LinearLayout button(Context context, String label, int iconType, boolean private LinearLayout card(Context context) { LinearLayout card = new LinearLayout(context); card.setOrientation(VERTICAL); - card.setBackground(null); - LineTheme.padding(card, 0, 16, 0, 24); + card.setBackground(LineTheme.rounded(context, LineTheme.SURFACE_ELEVATED, 12)); + LineTheme.padding(card, LineTheme.LG, LineTheme.LG, LineTheme.LG, LineTheme.LG); return card; } @@ -214,7 +214,7 @@ private TextView desc(Context context, String text) { private LinearLayout.LayoutParams formParams(Context context) { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - params.topMargin = LineTheme.dp(context, 24); + params.topMargin = LineTheme.dp(context, LineTheme.MD); return params; } diff --git a/app/src/main/java/cn/lineai/ui/component/SwitchRowView.java b/app/src/main/java/cn/lineai/ui/component/SwitchRowView.java index f596085a..1aeb5501 100644 --- a/app/src/main/java/cn/lineai/ui/component/SwitchRowView.java +++ b/app/src/main/java/cn/lineai/ui/component/SwitchRowView.java @@ -40,17 +40,15 @@ public SwitchRowView(Context context, int iconType, String label, String desc, b labels.addView(title, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); if (desc != null && desc.length() > 0) { - TextView description = LineTheme.text(context, desc, 14, LineTheme.TEXT_TERTIARY, Typeface.NORMAL); + TextView description = LineTheme.text(context, desc, LineTheme.FONT_XS, LineTheme.TEXT_TERTIARY, Typeface.NORMAL); LinearLayout.LayoutParams descParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - descParams.topMargin = LineTheme.dp(context, 6); + descParams.topMargin = LineTheme.dp(context, 2); labels.addView(description, descParams); } Switch toggle = new Switch(context); toggle.setChecked(value); toggle.setContentDescription(label); - toggle.setMinimumHeight(LineTheme.dp(context, 48)); - setBackground(LineTheme.pressable(context)); tintSwitch(toggle, listener); addView(toggle, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); setOnClickListener(v -> toggle.setChecked(!toggle.isChecked())); diff --git a/app/src/main/java/cn/lineai/ui/component/ThemeSettingsScreenView.java b/app/src/main/java/cn/lineai/ui/component/ThemeSettingsScreenView.java index effb09d3..59539f22 100644 --- a/app/src/main/java/cn/lineai/ui/component/ThemeSettingsScreenView.java +++ b/app/src/main/java/cn/lineai/ui/component/ThemeSettingsScreenView.java @@ -176,9 +176,9 @@ private void addCustomHeader(LinearLayout content) { LinearLayout header = new LinearLayout(context); header.setOrientation(HORIZONTAL); header.setGravity(Gravity.CENTER_VERTICAL); - LineTheme.padding(header, 16, 0, 16, 0); + LineTheme.padding(header, LineTheme.LG, 0, LineTheme.LG, 0); SectionHeaderView title = new SectionHeaderView(context, getResources().getString(R.string.screen_theme_custom_colors)); - LineTheme.padding(title, 0, 0, 8, 0); + LineTheme.padding(title, 0, 0, LineTheme.SM, 0); header.addView(title, new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f)); IconButtonView reset = new IconButtonView(context, IconButtonView.ROTATE_CCW); diff --git a/app/src/main/java/cn/lineai/ui/component/ToolSettingsScreenView.java b/app/src/main/java/cn/lineai/ui/component/ToolSettingsScreenView.java index 18c52427..6b738fd2 100644 --- a/app/src/main/java/cn/lineai/ui/component/ToolSettingsScreenView.java +++ b/app/src/main/java/cn/lineai/ui/component/ToolSettingsScreenView.java @@ -46,7 +46,7 @@ public ToolSettingsScreenView( this.imageUnderstandingModelLabel = imageUnderstandingModelLabel == null ? "" : imageUnderstandingModelLabel.trim(); this.imageGenerationModelLabel = imageGenerationModelLabel == null ? "" : imageGenerationModelLabel.trim(); LinearLayout content = getContent(); - LineTheme.padding(content, 28, 8, 28, 48); + LineTheme.padding(content, LineTheme.LG, LineTheme.LG, LineTheme.LG, 100); addSectionHeader(content, context.getString(R.string.screen_tools_section_images)); addImageUnderstanding(content); @@ -316,8 +316,8 @@ private WebSearchConfig readWebSearchConfig( private LinearLayout card(Context context) { LinearLayout card = new LinearLayout(context); card.setOrientation(LinearLayout.VERTICAL); - card.setBackground(null); - LineTheme.padding(card, 0, 16, 0, 24); + card.setBackground(LineTheme.rounded(context, LineTheme.SURFACE_ELEVATED, 12)); + LineTheme.padding(card, LineTheme.LG, LineTheme.LG, LineTheme.LG, LineTheme.LG); return card; } diff --git a/app/src/test/java/cn/lineai/ui/component/SecondaryScreenLayoutTest.java b/app/src/test/java/cn/lineai/ui/component/SecondaryScreenLayoutTest.java index 9fd4b577..469dab85 100644 --- a/app/src/test/java/cn/lineai/ui/component/SecondaryScreenLayoutTest.java +++ b/app/src/test/java/cn/lineai/ui/component/SecondaryScreenLayoutTest.java @@ -228,11 +228,11 @@ private void layout(View view,int width,int height) { screenshot(view, "storage-restored"); } - @Test public void extensionsHaveGapsAndExecutionCardsHideInternalIds() throws Exception { + @Test public void extensionsUseRestoredCardSpacingAndExecutionCardsHideInternalIds() throws Exception { View view = screen("Extensions"); layout(view, 390, 844); ViewGroup list = (ViewGroup) find(view, android.widget.ScrollView.class).getChildAt(0); for (int i = 1; i < list.getChildCount(); i++) - assertTrue(list.getChildAt(i).getTop() - list.getChildAt(i - 1).getBottom() >= 12); + assertTrue(list.getChildAt(i).getTop() - list.getChildAt(i - 1).getBottom() >= 8); screenshot(view, "extensions-spaced"); McpToolConfig config = new McpToolConfig("files", "文件操作", "读取和编辑工作区", true, new String[]{"internal_tool_id"}); diff --git a/app/src/test/java/cn/lineai/ui/component/UiCorrectionsTest.java b/app/src/test/java/cn/lineai/ui/component/UiCorrectionsTest.java index 52520eb1..7dae354a 100644 --- a/app/src/test/java/cn/lineai/ui/component/UiCorrectionsTest.java +++ b/app/src/test/java/cn/lineai/ui/component/UiCorrectionsTest.java @@ -113,14 +113,21 @@ private void assertMatchingHeaderActions(View page,int rightType) { assertEquals(22,iconSize(left));assertEquals(iconSize(left),iconSize(right)); assertEquals(left.getWidth(),right.getWidth());assertEquals(left.getHeight(),right.getHeight()); } - @Test public void settingsOptionsAndProvidersHaveVisibleGaps() throws Exception { + @Test public void settingsRowsUseRestoredGroupsAndStandaloneCardsKeepGaps() throws Exception { LLMSettingsScreenView llm=new LLMSettingsScreenView(activity,null,listener(LLMSettingsScreenView.Listener.class));layout(llm); - List rows=all(llm,OptionRowView.class); - assertTrue(rows.get(1).getTop()-rows.get(0).getBottom()>=8); - assertNotNull(rows.get(1).getBackground());screenshot(llm,"reasoning-options"); + List sections=all(llm,SettingsSectionView.class); + assertFalse(sections.isEmpty()); + LinearLayout group=sections.get(0).getGroup(); + assertNotNull(group.getBackground()); + assertTrue(group.getChildCount()>=2); + assertEquals(0,((LinearLayout.LayoutParams)group.getChildAt(1).getLayoutParams()).topMargin); + assertEquals(2,((ViewGroup)group.getChildAt(0)).getChildCount()); + screenshot(llm,"reasoning-options"); ModelAddOptionsScreenView providers=new ModelAddOptionsScreenView(activity,listener(ModelAddOptionsScreenView.Listener.class));layout(providers); - List actions=all(providers,ActionRowView.class); - for(int i=1;i=8); + List providerCards=all(providers,LinearLayout.class); + int visibleCards=0; + for(LinearLayout card:providerCards) if(card.isClickable()&&card.getBackground()!=null) visibleCards++; + assertTrue(visibleCards>=4); screenshot(providers,"providers"); McpSettingsState toolState=new McpSettingsState("local",Arrays.asList( new McpToolConfig("files","文件操作","读取、编辑和搜索项目文件",true,new String[]{"file_read","file_edit"}), @@ -146,6 +153,14 @@ private void assertMatchingHeaderActions(View page,int rightType) { +Math.abs(android.graphics.Color.blue(p.bg)-android.graphics.Color.blue(p.surfaceElevated)); assertTrue(mode+": card and app colors too close",delta>=45); LineTheme.apply(p);SettingsScreenView settings=new SettingsScreenView(activity,listener(SettingsScreenView.Listener.class));layout(settings); + TextView modelTitle=text(settings,activity.getString(R.string.settings_row_models_title)); + LinearLayout labels=(LinearLayout)modelTitle.getParent(); + assertTrue(all(labels,TextView.class).size()>=2); + LinearLayout item=(LinearLayout)labels.getParent(); + assertEquals(36,item.getChildAt(0).getLayoutParams().width); + assertNotNull(item.getChildAt(0).getBackground()); + ViewGroup wrapper=(ViewGroup)item.getParent(); + assertNotNull(((View)wrapper.getParent()).getBackground()); screenshot(settings,"settings-"+mode); } LineTheme.apply(ThemePalette.forMode("light")); diff --git a/update.md b/update.md index a20939ae..b7f72d65 100644 --- a/update.md +++ b/update.md @@ -40,7 +40,7 @@ ### 设置、主题与抽屉细节 -- **连续卡片间距与对比度** - 设置主页、主题选项、思考深度、模型列表、侧边栏设置组及扩展入口增加独立背景、边框或外间距;调整内置主题的卡片色阶,改善卡片与应用背景难以区分的问题 +- **设置结构恢复与配色更新** - 设置主页与共享设置子页恢复 UI 重构前的分区卡片、行内说明、彩色图标底板和分割线;继续使用新版 `LineTheme` 配色及内置主题色阶,保证卡片和应用背景清晰区分 - **主题编辑器保留完整操作** - 自定义主题继续使用完整的颜色编辑布局,保留已有配置入口,并补齐保存按钮右侧留白;浅色与深色系统栏颜色随主题适配 - **模型管理布局** - 缩小本地模型入口图标,调整预设提供商间隔和页面边距;模型选择短列表按内容高度展开,长列表受最大高度限制,模型长按菜单去除多余底部留白 - **工作区与目录抽屉** - 工作区选中背景铺满抽屉内容宽度,保留工作区选择与长按删除;目录选择确认操作置于面板内,限制窄屏和横屏下的面板尺寸