style: align native me surfaces with wechat ui

This commit is contained in:
kris
2026-03-30 12:26:14 +08:00
parent 038c2bd088
commit 9c15c30a41
18 changed files with 120 additions and 70 deletions

View File

@@ -36,8 +36,8 @@ android {
applicationId "com.hyzq.boss"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 16
versionName "2.5.3"
versionCode 17
versionName "2.5.4"
buildConfigField "String", "BOSS_API_BASE_URL", "\"https://boss.hyzq.net\""
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

View File

@@ -49,11 +49,13 @@ public class AiAccountsActivity extends BossScreenActivity {
JSONArray accounts = payload.optJSONArray("accounts");
JSONObject activeIdentity = payload.optJSONObject("activeIdentity");
replaceContent();
appendContent(BossUi.buildSoftPanel(
appendContent(BossUi.buildWechatMenuRow(
this,
"AI 账号",
"这里统一管理主 GPT、备用 GPT 与 API 容灾账号。",
"轻点条目可编辑,按钮可切换、校验或删除。"
"轻点条目可编辑,按钮可切换、校验或删除。",
null,
null
));
appendContent(buildActiveIdentityCard(activeIdentity));
appendContent(buildAccountsSection(accounts));
@@ -62,17 +64,27 @@ public class AiAccountsActivity extends BossScreenActivity {
private LinearLayout buildActiveIdentityCard(@Nullable JSONObject activeIdentity) {
if (activeIdentity == null) {
return BossUi.buildSoftPanel(this, "当前主控身份", "当前没有可用账号。", "请先新增或启用一个账号。");
return BossUi.buildWechatMenuRow(
this,
"当前主控身份",
"当前没有可用账号。",
"请先新增或启用一个账号。",
null,
null
);
}
String body = activeIdentity.optString("label", "AI 账号")
+ " · " + activeIdentity.optString("displayName", "-")
+ "\n" + activeIdentity.optString("roleLabel", "-")
+ " · " + activeIdentity.optString("providerLabel", "-");
return BossUi.buildSoftPanel(
String subtitle = activeIdentity.optString("label", "AI 账号")
+ " · " + activeIdentity.optString("displayName", "-");
String meta = activeIdentity.optString("roleLabel", "-")
+ " · " + activeIdentity.optString("providerLabel", "-")
+ " · " + activeIdentity.optString("statusLabel", "-");
return BossUi.buildWechatMenuRow(
this,
"当前主控身份",
body,
activeIdentity.optString("statusLabel", "-")
subtitle,
meta,
null,
null
);
}

View File

@@ -47,6 +47,28 @@ public final class BossUi {
COMPACT_ICON
}
public static String formatRoleLabel(@Nullable String rawRole) {
if (TextUtils.isEmpty(rawRole)) {
return "";
}
switch (rawRole) {
case "highest_admin":
return "最高管理员";
case "admin":
return "管理员";
case "member":
return "成员";
case "primary":
return "主 GPT";
case "backup":
return "备用 GPT";
case "api_fallback":
return "API 容灾";
default:
return rawRole;
}
}
public static void applyTopActionButtonStyle(Context context, Button button, TopActionButtonStyle style) {
if (style == TopActionButtonStyle.COMPACT_ICON) {
button.setBackgroundResource(R.drawable.bg_secondary_button);
@@ -232,7 +254,11 @@ public final class BossUi {
@Nullable String badge,
@Nullable View.OnClickListener listener
) {
return buildListRow(context, title, subtitle, meta, badge, listener);
LinearLayout row = buildListRow(context, title, subtitle, meta, badge, listener);
row.setBackgroundColor(Color.WHITE);
row.setElevation(0f);
row.setPadding(dp(context, 18), dp(context, 15), dp(context, 18), dp(context, 15));
return row;
}
public static LinearLayout buildSimpleProfileHeader(
@@ -248,23 +274,21 @@ public final class BossUi {
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.leftMargin = dp(context, 12);
params.rightMargin = dp(context, 12);
params.bottomMargin = dp(context, 12);
params.bottomMargin = dp(context, 10);
card.setLayoutParams(params);
card.setPadding(dp(context, 16), dp(context, 16), dp(context, 16), dp(context, 16));
card.setBackground(createRoundedBackground(Color.WHITE, dp(context, 18)));
card.setElevation(dp(context, 1));
card.setPadding(dp(context, 20), dp(context, 18), dp(context, 20), dp(context, 18));
card.setBackgroundColor(Color.WHITE);
card.setElevation(0f);
TextView avatar = new TextView(context);
LinearLayout.LayoutParams avatarParams = new LinearLayout.LayoutParams(dp(context, 64), dp(context, 64));
LinearLayout.LayoutParams avatarParams = new LinearLayout.LayoutParams(dp(context, 70), dp(context, 70));
avatar.setLayoutParams(avatarParams);
avatar.setGravity(Gravity.CENTER);
avatar.setText(firstLetter(name));
avatar.setTextSize(28);
avatar.setTextSize(30);
avatar.setTypeface(Typeface.DEFAULT_BOLD);
avatar.setTextColor(context.getColor(R.color.boss_green));
avatar.setBackground(createRoundedBackground(Color.parseColor("#DFF3E8"), dp(context, 18)));
avatar.setBackground(createRoundedBackground(Color.parseColor("#DFF3E8"), dp(context, 35)));
card.addView(avatar);
LinearLayout textWrap = new LinearLayout(context);
@@ -279,16 +303,16 @@ public final class BossUi {
TextView titleView = new TextView(context);
titleView.setText(TextUtils.isEmpty(name) ? "我的" : name);
titleView.setTextSize(17);
titleView.setTextSize(22);
titleView.setTypeface(Typeface.DEFAULT_BOLD);
titleView.setTextColor(context.getColor(R.color.boss_text_primary));
textWrap.addView(titleView);
TextView subtitleView = new TextView(context);
subtitleView.setText(subtitle);
subtitleView.setTextSize(13);
subtitleView.setTextSize(14);
subtitleView.setTextColor(context.getColor(R.color.boss_text_muted));
subtitleView.setPadding(0, dp(context, 4), 0, 0);
subtitleView.setPadding(0, dp(context, 5), 0, 0);
textWrap.addView(subtitleView);
if (!TextUtils.isEmpty(detail)) {
@@ -296,7 +320,7 @@ public final class BossUi {
detailView.setText(detail);
detailView.setTextSize(12);
detailView.setTextColor(context.getColor(R.color.boss_text_soft));
detailView.setPadding(0, dp(context, 6), 0, 0);
detailView.setPadding(0, dp(context, 5), 0, 0);
textWrap.addView(detailView);
}
@@ -543,13 +567,11 @@ public final class BossUi {
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.leftMargin = dp(context, 12);
params.rightMargin = dp(context, 12);
params.bottomMargin = dp(context, 12);
params.bottomMargin = dp(context, 1);
card.setLayoutParams(params);
card.setPadding(dp(context, 14), dp(context, 14), dp(context, 14), dp(context, 14));
card.setBackground(createRoundedBackground(Color.WHITE, dp(context, 18)));
card.setElevation(dp(context, 1));
card.setPadding(dp(context, 16), dp(context, 12), dp(context, 16), dp(context, 12));
card.setBackgroundColor(Color.WHITE);
card.setElevation(0f);
if (listener != null) {
card.setClickable(true);
card.setFocusable(true);
@@ -566,12 +588,12 @@ public final class BossUi {
1f
);
centerParams.leftMargin = dp(context, 12);
centerParams.rightMargin = dp(context, 8);
centerParams.rightMargin = dp(context, 10);
centerColumn.setLayoutParams(centerParams);
TextView titleView = new TextView(context);
titleView.setText(TextUtils.isEmpty(row.threadTitle) ? "未命名会话" : row.threadTitle);
titleView.setTextSize(18);
titleView.setTextSize(17);
titleView.setTypeface(Typeface.DEFAULT_BOLD);
titleView.setTextColor(context.getColor(R.color.boss_text_primary));
titleView.setMaxLines(1);
@@ -591,10 +613,10 @@ public final class BossUi {
TextView previewView = new TextView(context);
previewView.setText(TextUtils.isEmpty(row.lastMessagePreview) ? "暂无消息" : row.lastMessagePreview);
previewView.setTextSize(14);
previewView.setTextSize(13);
previewView.setTextColor(context.getColor(R.color.boss_text_soft));
previewView.setPadding(0, dp(context, 5), 0, 0);
previewView.setMaxLines(2);
previewView.setPadding(0, dp(context, 4), 0, 0);
previewView.setMaxLines(1);
previewView.setEllipsize(TextUtils.TruncateAt.END);
centerColumn.addView(previewView);

View File

@@ -502,11 +502,14 @@ public class MainActivity extends AppCompatActivity {
String account = sessionData == null
? apiClient.getAccountLabel()
: sessionData.optString("account", apiClient.getAccountLabel());
String roleLabel = sessionData == null
? ""
: BossUi.formatRoleLabel(sessionData.optString("roleLabel", sessionData.optString("role", "")));
screenContent.addView(BossUi.buildSimpleProfileHeader(
this,
displayName,
"ChatGPT Plus · 主账号",
"主控账号已启用安全保护 · " + account
account,
(roleLabel.isEmpty() ? "主控账号已启用安全保护" : roleLabel + " · 主控账号已启用安全保护")
));
for (WechatSurfaceMapper.MeMenuItem item : WechatSurfaceMapper.rootMeMenuItems()) {

View File

@@ -50,13 +50,15 @@ public class OpsCenterActivity extends BossScreenActivity {
}
private void renderOpsTab(JSONObject ops) {
contentRoot.addView(BossUi.buildSoftPanel(
contentRoot.addView(BossUi.buildWechatMenuRow(
this,
"巡检状态",
ops.optString("mode", "idle").equals("active")
? "active当前存在风险线程或未关闭运维工单。"
: "idle当前没有高风险工单保持低频巡检。",
"这里只保留修复与验证的轻量入口。"
"这里只保留修复与验证的轻量入口。",
null,
null
));
JSONArray faults = ops.optJSONArray("faults");

View File

@@ -34,18 +34,20 @@ public class SecurityActivity extends BossScreenActivity {
private void renderSecurity(@Nullable JSONObject session) {
replaceContent();
appendContent(BossUi.buildSoftPanel(
appendContent(BossUi.buildWechatMenuRow(
this,
"当前登录模式",
"当前客户端仍使用快速进入模式。",
"需要更严格认证时,再切回账号密码或验证码登录。"
"需要更严格认证时,再切回账号密码或验证码登录。",
null,
null
));
if (session != null) {
appendContent(BossUi.buildWechatMenuRow(
this,
"当前会话",
"账号 " + session.optString("account", "-")
+ " · " + session.optString("role", "-"),
+ " · " + BossUi.formatRoleLabel(session.optString("role", "-")),
"登录方式 " + session.optString("loginMethod", "-")
+ " · 到期 " + session.optString("expiresAt", "-"),
null,

View File

@@ -69,11 +69,13 @@ public class SettingsActivity extends BossScreenActivity {
preferredEntrySpinner.setAdapter(adapter);
}
replaceContent(BossUi.buildSoftPanel(
replaceContent(BossUi.buildWechatMenuRow(
this,
"偏好设置",
"调整默认首页和提醒行为。",
"保存后会直接写入 /api/v1/settings。"
"保存后会直接写入当前账号设置",
null,
null
));
appendContent(BossUi.buildFormCell(this, "实时刷新", "会话、设备和 OTA 状态变化时自动更新", liveUpdatesSwitch));

View File

@@ -119,11 +119,13 @@ public class SkillInventoryActivity extends BossScreenActivity {
if (device != null) {
deviceName = device.optString("name", deviceId);
configureScreen("技能", deviceName);
appendContent(BossUi.buildSoftPanel(
appendContent(BossUi.buildWechatMenuRow(
this,
deviceName,
"当前页按设备查看 Skill 清单。",
"Skill 由 local-agent 从本机 ~/.codex/skills 扫描并同步。"
"Skill 由 local-agent 从本机 ~/.codex/skills 扫描并同步。",
null,
null
));
}

View File

@@ -28,7 +28,7 @@ public class BossUiRootSurfaceTest {
new JSONObject()
.put("displayName", "Kris")
.put("account", "17600003315")
.put("role", "最高管理员")
.put("role", "highest_admin")
);
ReflectionHelpers.callInstanceMethod(activity, "renderMeRoot");
@@ -39,7 +39,7 @@ public class BossUiRootSurfaceTest {
assertEquals("资料头不应保留浮层卡片感", 0f, header.getElevation(), 0.01f);
assertTrue(viewTreeContainsText(header, "Kris"));
assertTrue(viewTreeContainsText(header, "17600003315"));
assertTrue(viewTreeContainsText(header, "ChatGPT Plus · 主账号"));
assertTrue(viewTreeContainsText(header, "最高管理员"));
assertTrue(viewTreeContainsText(header, "主控账号已启用安全保护"));
assertTrue(viewTreeContainsText(content, "账号与安全"));
@@ -48,12 +48,17 @@ public class BossUiRootSurfaceTest {
assertTrue(viewTreeContainsText(content, "AI 账号"));
assertTrue(viewTreeContainsText(content, "技能"));
assertTrue(viewTreeContainsText(content, "关于"));
for (int i = 1; i < content.getChildCount(); i += 1) {
View row = content.getChildAt(i);
assertTrue("我的页菜单应整行可点", row.isClickable());
}
}
private static boolean viewTreeContainsText(View root, String expectedText) {
if (root instanceof TextView) {
CharSequence text = ((TextView) root).getText();
if (expectedText.contentEquals(text)) {
if (text != null && text.toString().contains(expectedText)) {
return true;
}
}