style: align native me surfaces with wechat ui
This commit is contained in:
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user