diff --git a/android/app/src/main/java/com/hyzq/boss/AboutActivity.java b/android/app/src/main/java/com/hyzq/boss/AboutActivity.java index 0eec69a..f4e192c 100644 --- a/android/app/src/main/java/com/hyzq/boss/AboutActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/AboutActivity.java @@ -10,8 +10,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.provider.Settings; -import android.widget.Button; -import android.widget.LinearLayout; import androidx.annotation.Nullable; @@ -40,7 +38,7 @@ public class AboutActivity extends BossScreenActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - configureScreen("关于 / OTA", "原生版本中心"); + configureScreen("关于", "版本与更新"); IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { registerReceiver(otaDownloadReceiver, filter, Context.RECEIVER_NOT_EXPORTED); @@ -89,51 +87,58 @@ public class AboutActivity extends BossScreenActivity { replaceContent(); otaPayload = ota; if (user != null) { - appendContent(BossUi.buildCard( + appendContent(BossUi.buildListRow( this, "当前版本", user.optString("version", "-") - + "\n当前账号:" + user.optString("account", "-") - + "\n绑定 Codex:" + user.optString("boundCodexNodeLabel", "未绑定"), - session == null ? "-" : "会话到期 " + session.optString("expiresAt", "-") + + " · 账号 " + user.optString("account", "-"), + "绑定 Codex " + user.optString("boundCodexNodeLabel", "未绑定") + + (session == null ? "" : " · 会话到期 " + session.optString("expiresAt", "-")), + null, + null )); } JSONObject availableRelease = ota.optJSONObject("availableRelease"); - String otaBody = availableRelease == null + String otaSubtitle = availableRelease == null ? "当前已经是最新版本。" - : availableRelease.optString("version", "未知版本") - + "\n" + availableRelease.optString("summary", "暂无摘要") - + "\n文件:" + availableRelease.optString("packageFileName", "-"); - appendContent(BossUi.buildCard( + : "可用版本 " + availableRelease.optString("version", "未知版本"); + String otaMeta = availableRelease == null + ? "当前版本 " + ota.optString("currentVersion", "-") + : availableRelease.optString("summary", "暂无摘要") + + " · 文件 " + availableRelease.optString("packageFileName", "-"); + appendContent(BossUi.buildListRow( this, "OTA 状态", - otaBody, - "当前版本 " + ota.optString("currentVersion", "-") + otaSubtitle, + otaMeta, + availableRelease == null ? null : "NEW", + null )); - LinearLayout actionCard = BossUi.buildCard(this, "OTA 操作", "可在原生页直接检查更新、登记 OTA 并下载 APK。", "当前接口:/api/v1/user/ota"); - Button check = BossUi.buildPrimaryButton(this, "检查更新"); - check.setOnClickListener(v -> performOtaAction("check")); - actionCard.addView(check); - Button apply = BossUi.buildSecondaryButton(this, "登记应用 OTA"); - apply.setOnClickListener(v -> performOtaAction("apply")); - actionCard.addView(apply); - Button download = BossUi.buildSecondaryButton(this, "应用内下载 APK"); - download.setOnClickListener(v -> downloadLatestApk()); - actionCard.addView(download); - appendContent(actionCard); + appendContent(BossUi.buildMenuRow(this, "检查更新", "拉取最新 OTA 状态", null, v -> performOtaAction("check"))); + appendContent(BossUi.buildMenuRow(this, "登记应用 OTA", "把当前已应用版本写回服务端", null, v -> performOtaAction("apply"))); + appendContent(BossUi.buildMenuRow(this, "应用内下载 APK", "下载最新安装包并拉起系统安装器", null, v -> downloadLatestApk())); + appendContent(BossUi.buildMenuRow( + this, + WechatSurfaceMapper.advancedEntryTitle(), + "进入运维对话、审计与修复入口", + null, + v -> startActivity(new Intent(this, OpsCenterActivity.class)) + )); JSONArray logs = ota.optJSONArray("logs"); if (logs != null) { for (int i = 0; i < logs.length(); i++) { JSONObject log = logs.optJSONObject(i); if (log == null) continue; - appendContent(BossUi.buildCard( + appendContent(BossUi.buildListRow( this, log.optString("version", "OTA"), log.optString("summary", ""), - log.optString("status", "-") + " · " + log.optString("createdAt", "-") + log.optString("status", "-") + " · " + log.optString("createdAt", "-"), + null, + null )); } } diff --git a/android/app/src/main/java/com/hyzq/boss/AiAccountsActivity.java b/android/app/src/main/java/com/hyzq/boss/AiAccountsActivity.java index 946ffc8..2d13063 100644 --- a/android/app/src/main/java/com/hyzq/boss/AiAccountsActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/AiAccountsActivity.java @@ -1,7 +1,6 @@ package com.hyzq.boss; import android.os.Bundle; -import android.view.View; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.LinearLayout; @@ -20,14 +19,12 @@ public class AiAccountsActivity extends BossScreenActivity { private static final String[] PROVIDER_VALUES = {"master_codex_node", "openai_api"}; private static final String[] PROVIDER_LABELS = {"Master Codex Node", "OpenAI API"}; - private LinearLayout accountList; - @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); configureScreen("AI 账号", "主 GPT / 备用 GPT / API 容灾"); setHeaderAction("新增", v -> openAccountEditor(null, null)); - replaceContent(buildIntroCard(), buildAccountListShell()); + replaceContent(); reload(); } @@ -48,56 +45,46 @@ public class AiAccountsActivity extends BossScreenActivity { }); } - private LinearLayout buildIntroCard() { - return BossUi.buildCard( - this, - "账号说明", - "当前页面管理 Boss 的主控 AI 账号。主链路优先使用已绑定电脑上的 Master Codex Node,API 容灾在同页可补充配置。", - "支持新增、编辑、激活、校验和删除" - ); - } - - private LinearLayout buildAccountListShell() { - LinearLayout wrapper = new LinearLayout(this); - wrapper.setOrientation(LinearLayout.VERTICAL); - accountList = new LinearLayout(this); - accountList.setOrientation(LinearLayout.VERTICAL); - wrapper.addView(accountList); - return wrapper; - } - private void renderAccounts(JSONObject payload) { JSONArray accounts = payload.optJSONArray("accounts"); JSONObject activeIdentity = payload.optJSONObject("activeIdentity"); - JSONArray switchHistory = payload.optJSONArray("switchHistory"); - - accountList.removeAllViews(); - replaceContent(buildIntroCard(), buildActiveIdentityCard(activeIdentity), buildAccountsSection(accounts), buildSwitchHistoryCard(switchHistory)); + replaceContent(); + appendContent(BossUi.buildListRow( + this, + "账号管理", + "管理主 GPT、备用 GPT 与 API 容灾。", + "支持新增、编辑、激活、校验和删除。", + null, + null + )); + appendContent(buildActiveIdentityCard(activeIdentity)); + appendContent(buildAccountsSection(accounts)); setRefreshing(false); } private LinearLayout buildActiveIdentityCard(@Nullable JSONObject activeIdentity) { - String body = activeIdentity == null - ? "当前没有可用的主控身份。" - : activeIdentity.optString("label", "AI 账号") - + "\n" + activeIdentity.optString("displayName", "-") - + "\n" + activeIdentity.optString("providerLabel", "-") - + (activeIdentity.optString("nodeLabel").isEmpty() ? "" : "\n节点:" + activeIdentity.optString("nodeLabel")); - String meta = activeIdentity == null - ? "请先配置一个可用账号" - : activeIdentity.optString("roleLabel", "-") + " · " + activeIdentity.optString("statusLabel", "-"); - return BossUi.buildCard(this, "当前主控身份", body, meta); + if (activeIdentity == null) { + return BossUi.buildListRow(this, "当前主控身份", "当前没有可用账号。", "请先新增或启用一个账号。", null, null); + } + String subtitle = activeIdentity.optString("label", "AI 账号") + + " · " + activeIdentity.optString("displayName", "-"); + String meta = activeIdentity.optString("roleLabel", "-") + + " · " + activeIdentity.optString("providerLabel", "-") + + " · " + activeIdentity.optString("statusLabel", "-"); + return BossUi.buildListRow(this, "当前主控身份", subtitle, meta, "当前", null); } private LinearLayout buildAccountsSection(@Nullable JSONArray accounts) { LinearLayout section = new LinearLayout(this); section.setOrientation(LinearLayout.VERTICAL); - section.addView(BossUi.buildCard( + section.addView(BossUi.buildListRow( this, "账号列表", - accounts == null || accounts.length() == 0 ? "当前还没有 AI 账号。" : "点击卡片可编辑,按钮可激活 / 校验 / 删除。", - "当前 API:/api/v1/accounts" + accounts == null || accounts.length() == 0 ? "当前还没有 AI 账号。" : "点开可编辑,按钮可激活、校验或删除。", + "当前 API:/api/v1/accounts", + null, + null )); if (accounts == null || accounts.length() == 0) { @@ -118,20 +105,25 @@ public class AiAccountsActivity extends BossScreenActivity { String meta = account.optString("roleLabel", "-") + " · " + account.optString("providerLabel", "-") + " · " + statusLabel - + (account.optBoolean("isActive") ? " · 当前主控" : "") + (account.optBoolean("apiKeyConfigured") ? " · 已配置 Key" : ""); - String body = account.optString("displayName", "-") - + "\n账号:" + account.optString("accountIdentifier", "-") - + (account.optString("nodeLabel").isEmpty() ? "" : "\n节点:" + account.optString("nodeLabel")) - + (account.optString("loginStatusNote").isEmpty() ? "" : "\n" + account.optString("loginStatusNote")); + StringBuilder subtitle = new StringBuilder(account.optString("displayName", "-")); + if (!account.optString("accountIdentifier").isEmpty()) { + subtitle.append(" · ").append(account.optString("accountIdentifier", "-")); + } + if (!account.optString("nodeLabel").isEmpty()) { + subtitle.append(" · ").append(account.optString("nodeLabel", "-")); + } - LinearLayout card = BossUi.buildCard( + LinearLayout card = new LinearLayout(this); + card.setOrientation(LinearLayout.VERTICAL); + card.addView(BossUi.buildListRow( this, account.optString("label", "未命名账号"), - body, + subtitle.toString(), meta, + account.optBoolean("isActive") ? "当前" : null, v -> openAccountEditor(account, null) - ); + )); Button activate = BossUi.buildPrimaryButton(this, account.optBoolean("isActive") ? "已激活" : "设为当前主控"); activate.setEnabled(!account.optBoolean("isActive")); @@ -153,33 +145,6 @@ public class AiAccountsActivity extends BossScreenActivity { return card; } - private LinearLayout buildSwitchHistoryCard(@Nullable JSONArray switchHistory) { - LinearLayout section = new LinearLayout(this); - section.setOrientation(LinearLayout.VERTICAL); - section.addView(BossUi.buildCard( - this, - "切换历史", - switchHistory == null || switchHistory.length() == 0 ? "当前没有切换记录。" : "最近切换记录会保留 40 条。", - "用于追踪主控身份变化" - )); - - if (switchHistory == null || switchHistory.length() == 0) { - section.addView(BossUi.buildEmptyCard(this, "当前没有 AI 账号切换历史。")); - return section; - } - - for (int i = 0; i < switchHistory.length(); i++) { - JSONObject record = switchHistory.optJSONObject(i); - if (record == null) continue; - String body = "从 " + record.optString("fromLabel", "无") - + "\n到 " + record.optString("toLabel", "-") - + "\n原因:" + record.optString("reason", "-"); - String meta = record.optString("role", "-") + " · " + record.optString("switchedAt", "-"); - section.addView(BossUi.buildCard(this, "切换记录", body, meta)); - } - return section; - } - private void openAccountEditor(@Nullable JSONObject existing, @Nullable String apiKeyHint) { final android.widget.EditText labelInput = BossUi.buildInput(this, "标签,例如 主 GPT", false); final android.widget.EditText displayNameInput = BossUi.buildInput(this, "显示名称", false); diff --git a/android/app/src/main/java/com/hyzq/boss/DeviceDetailActivity.java b/android/app/src/main/java/com/hyzq/boss/DeviceDetailActivity.java index 27da0f8..2af9e09 100644 --- a/android/app/src/main/java/com/hyzq/boss/DeviceDetailActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/DeviceDetailActivity.java @@ -2,7 +2,6 @@ package com.hyzq.boss; import android.content.Intent; import android.os.Bundle; -import android.widget.Button; import android.widget.LinearLayout; import androidx.annotation.Nullable; @@ -24,7 +23,7 @@ public class DeviceDetailActivity extends BossScreenActivity { deviceId = getIntent().getStringExtra(EXTRA_DEVICE_ID); deviceName = getIntent().getStringExtra(EXTRA_DEVICE_NAME); configureScreen(deviceName == null ? "设备详情" : deviceName, "原生设备详情"); - setHeaderAction("编辑", v -> openEditDialog()); + hideHeaderAction(); reload(); } @@ -48,8 +47,6 @@ public class DeviceDetailActivity extends BossScreenActivity { private void renderDevice(JSONObject payload) { JSONObject workspace = payload.optJSONObject("workspace"); JSONObject device = workspace == null ? null : workspace.optJSONObject("selectedDevice"); - JSONArray relatedThreads = workspace == null ? null : workspace.optJSONArray("relatedThreads"); - JSONObject enrollment = workspace == null ? null : workspace.optJSONObject("activeEnrollment"); replaceContent(); if (device == null) { @@ -60,53 +57,38 @@ public class DeviceDetailActivity extends BossScreenActivity { deviceName = device.optString("name", deviceId); configureScreen(deviceName, device.optString("endpoint", "设备详情")); - appendContent(BossUi.buildCard( + appendContent(BossUi.buildListRow( this, device.optString("name", "设备"), - device.optString("note", "暂无备注"), - "状态 " + device.optString("status", "unknown") - + " · 账号 " + device.optString("account", "-") - + " · 5h " + device.optInt("quota5h", 0) - + " · 7d " + device.optInt("quota7d", 0) + buildDeviceSubtitle(device), + buildDeviceMeta(device), + null, + null )); - Button skillsButton = BossUi.buildPrimaryButton(this, "查看技能"); - skillsButton.setOnClickListener(v -> openSkills()); - appendContent(skillsButton); - - if (relatedThreads != null && relatedThreads.length() > 0) { - for (int i = 0; i < relatedThreads.length(); i++) { - JSONObject thread = relatedThreads.optJSONObject(i); - if (thread == null) continue; - appendContent(BossUi.buildCard( - this, - thread.optString("title", "线程"), - thread.optString("summary", ""), - thread.optString("workerId", "-") - + " · " + thread.optInt("contextBudgetRemainingPct", 0) + "%" - + " · " + thread.optString("contextBudgetLevel", "safe"), - v -> openThread(thread.optString("threadId")) - )); - } - } - - if (enrollment != null) { - appendContent(BossUi.buildCard( - this, - "当前绑定草稿", - "pairingCode " + enrollment.optString("pairingCode", "-") - + "\ntoken " + enrollment.optString("token", "-"), - enrollment.optString("status", "ready") - + " · 到期 " + enrollment.optString("expiresAt", "-") - )); - } + appendContent(BossUi.buildMenuRow(this, "查看技能", "查看当前设备同步的 Skill 清单", null, v -> openSkills())); + appendContent(BossUi.buildMenuRow(this, "编辑", "修改设备名称、备注和项目列表", null, v -> openEditDialog())); setRefreshing(false); } - private void openThread(String threadId) { - Intent intent = new Intent(this, ThreadDetailActivity.class); - intent.putExtra(ThreadDetailActivity.EXTRA_THREAD_ID, threadId); - startActivity(intent); + private String buildDeviceSubtitle(JSONObject device) { + String note = device.optString("note", ""); + if (!note.isEmpty()) { + return note; + } + return "状态 " + device.optString("status", "unknown") + " · 账号 " + device.optString("account", "-"); + } + + private @Nullable String buildDeviceMeta(JSONObject device) { + String endpoint = device.optString("endpoint", ""); + if (!endpoint.isEmpty()) { + return endpoint; + } + JSONArray projects = device.optJSONArray("projects"); + if (projects == null || projects.length() == 0) { + return null; + } + return "项目 " + joinArray(projects); } private void openSkills() { diff --git a/android/app/src/main/java/com/hyzq/boss/MainActivity.java b/android/app/src/main/java/com/hyzq/boss/MainActivity.java index 7fe2229..bc683ad 100644 --- a/android/app/src/main/java/com/hyzq/boss/MainActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/MainActivity.java @@ -344,12 +344,11 @@ public class MainActivity extends AppCompatActivity { private void renderDevicesRoot() { screenContent.removeAllViews(); - screenContent.addView(BossUi.buildListRow( + screenContent.addView(BossUi.buildMenuRow( this, "添加设备", "通过配对码接入新的生产设备", null, - null, v -> startActivity(new Intent(this, DeviceEnrollmentActivity.class)) )); @@ -363,8 +362,7 @@ public class MainActivity extends AppCompatActivity { if (item == null) continue; String deviceId = item.optString("id", ""); WechatSurfaceMapper.DeviceRow row = WechatSurfaceMapper.toDeviceRow(item); - String meta = "5h " + item.optInt("quota5h", 0) + " · 7d " + item.optInt("quota7d", 0); - screenContent.addView(BossUi.buildListRow(this, row.title, row.subtitle, meta, null, v -> { + screenContent.addView(BossUi.buildListRow(this, row.title, row.subtitle, null, null, v -> { if (deviceId.isEmpty()) { showMessage("缺少 deviceId"); return; @@ -382,12 +380,11 @@ public class MainActivity extends AppCompatActivity { String account = sessionData == null ? apiClient.getAccountLabel() : sessionData.optString("account", apiClient.getAccountLabel()); - String expiresAt = sessionData == null ? "-" : sessionData.optString("expiresAt", "-"); screenContent.addView(BossUi.buildListRow( this, displayName, "账号 " + account, - "会话到期 " + expiresAt, + null, null, null )); @@ -402,16 +399,6 @@ public class MainActivity extends AppCompatActivity { )); } - if (otaData != null) { - JSONObject availableRelease = otaData.optJSONObject("availableRelease"); - String body = "当前版本 " + otaData.optString("currentVersion", "-"); - String meta = availableRelease == null - ? "当前没有待安装版本" - : "可用版本 " + availableRelease.optString("version", "-") - + " · 文件 " + availableRelease.optString("packageFileName", "-"); - screenContent.addView(BossUi.buildCard(this, "OTA 状态", body, meta)); - } - Button logoutButton = BossUi.buildSecondaryButton(this, "退出登录"); logoutButton.setOnClickListener(v -> logout()); screenContent.addView(logoutButton); @@ -488,15 +475,15 @@ public class MainActivity extends AppCompatActivity { private String meDescriptionFor(String title) { switch (title) { case "账号与安全": - return "查看当前会话、登录模式和退出登录"; + return "查看当前会话与登录安全"; case "AI 账号": return "管理主 GPT、备用 GPT 与 API 容灾"; case "设置": - return "调整默认首页和刷新偏好"; + return "调整默认首页和提醒偏好"; case "技能": - return "按绑定设备查看 Skill 清单"; + return "按设备查看 Skill 清单"; case "关于": - return "查看版本、OTA 状态和当前绑定节点"; + return "查看版本、更新与高级入口"; default: return ""; } diff --git a/android/app/src/main/java/com/hyzq/boss/SecurityActivity.java b/android/app/src/main/java/com/hyzq/boss/SecurityActivity.java index 73b0c3a..a155f1c 100644 --- a/android/app/src/main/java/com/hyzq/boss/SecurityActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/SecurityActivity.java @@ -33,33 +33,34 @@ public class SecurityActivity extends BossScreenActivity { } private void renderSecurity(@Nullable JSONObject session) { - replaceContent( - BossUi.buildCard( - this, - "当前登录模式", - "当前登录页已临时切到免验证模式,点击登录会直接创建最高管理员会话。", - "后续如收口认证,再切回账号密码 / 验证码登录。" - ) - ); + replaceContent(); + appendContent(BossUi.buildListRow( + this, + "当前登录模式", + "当前客户端仍使用快速进入模式。", + "需要更严格认证时,再切回账号密码或验证码登录。", + null, + null + )); if (session != null) { - appendContent(BossUi.buildCard( + appendContent(BossUi.buildListRow( this, "当前会话", "账号 " + session.optString("account", "-") - + "\n角色 " + session.optString("role", "-") - + "\n登录方式 " + session.optString("loginMethod", "-"), - "到期 " + session.optString("expiresAt", "-") + + " · " + session.optString("role", "-"), + "登录方式 " + session.optString("loginMethod", "-") + + " · 到期 " + session.optString("expiresAt", "-"), + null, + null )); } - android.widget.Button devicesButton = BossUi.buildPrimaryButton(this, "打开设备页"); - devicesButton.setOnClickListener(v -> { + appendContent(BossUi.buildMenuRow(this, "打开设备页", "查看已绑定设备与状态", null, v -> { Intent intent = new Intent(this, MainActivity.class); intent.putExtra(MainActivity.EXTRA_INITIAL_TAB, "devices"); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); startActivity(intent); - }); - appendContent(devicesButton); + })); android.widget.Button logoutButton = BossUi.buildSecondaryButton(this, "退出登录"); logoutButton.setOnClickListener(v -> logout()); diff --git a/android/app/src/main/java/com/hyzq/boss/SettingsActivity.java b/android/app/src/main/java/com/hyzq/boss/SettingsActivity.java index 62936cf..de98f85 100644 --- a/android/app/src/main/java/com/hyzq/boss/SettingsActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/SettingsActivity.java @@ -43,14 +43,14 @@ public class SettingsActivity extends BossScreenActivity { } private void buildForm() { - replaceContent( - BossUi.buildCard( - this, - "设置说明", - "当前设置会持久化到 data/boss-state.json,下一线程接手不会丢失。", - "原生设置页直接走 /api/v1/settings" - ) - ); + replaceContent(BossUi.buildListRow( + this, + "偏好设置", + "调整默认首页和提醒行为。", + "保存后会直接写入 /api/v1/settings。", + null, + null + )); liveUpdatesSwitch = new SwitchCompat(this); liveUpdatesSwitch.setText("启用实时刷新"); @@ -69,12 +69,12 @@ public class SettingsActivity extends BossScreenActivity { ); preferredEntrySpinner.setAdapter(adapter); - LinearLayout card = BossUi.buildCard(this, "交互偏好", "可切换默认首页与提醒行为。", "保存后立即生效"); - card.addView(liveUpdatesSwitch); - card.addView(riskBadgesSwitch); - card.addView(confirmActionsSwitch); - card.addView(preferredEntrySpinner); - appendContent(card); + LinearLayout form = BossUi.buildCard(this, "交互偏好", "切换默认首页与提醒开关。", "保存后立即生效"); + form.addView(liveUpdatesSwitch); + form.addView(riskBadgesSwitch); + form.addView(confirmActionsSwitch); + form.addView(preferredEntrySpinner); + appendContent(form); } private void populate(@Nullable JSONObject settings) { diff --git a/android/app/src/main/java/com/hyzq/boss/SkillInventoryActivity.java b/android/app/src/main/java/com/hyzq/boss/SkillInventoryActivity.java index 24f1120..978ddcb 100644 --- a/android/app/src/main/java/com/hyzq/boss/SkillInventoryActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/SkillInventoryActivity.java @@ -65,11 +65,13 @@ public class SkillInventoryActivity extends BossScreenActivity { if (device != null) { deviceName = device.optString("name", deviceId); configureScreen("技能", deviceName); - appendContent(BossUi.buildCard( + appendContent(BossUi.buildListRow( this, deviceName, "当前页按设备查看 Skill 清单。", - "Skill 由 local-agent 从本机 ~/.codex/skills 扫描并同步。" + "Skill 由 local-agent 从本机 ~/.codex/skills 扫描并同步。", + null, + null )); } @@ -81,13 +83,17 @@ public class SkillInventoryActivity extends BossScreenActivity { for (int i = 0; i < skills.length(); i++) { JSONObject skill = skills.optJSONObject(i); if (skill == null) continue; - LinearLayout card = BossUi.buildCard( + LinearLayout card = new LinearLayout(this); + card.setOrientation(LinearLayout.VERTICAL); + card.addView(BossUi.buildListRow( this, skill.optString("name", "未命名 Skill"), skill.optString("description", "未提供说明"), skill.optString("category", "-") - + " · " + skill.optString("updatedAt", "-") - ); + + " · " + skill.optString("updatedAt", "-"), + null, + null + )); Button copyInvocation = BossUi.buildPrimaryButton(this, "复制调用语句"); copyInvocation.setOnClickListener(v -> BossUi.copyText(this, "Skill 调用", skill.optString("invocation", ""))); card.addView(copyInvocation); diff --git a/android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java b/android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java index a95321e..c5e8267 100644 --- a/android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java +++ b/android/app/src/main/java/com/hyzq/boss/WechatSurfaceMapper.java @@ -60,6 +60,10 @@ public final class WechatSurfaceMapper { return ROOT_ME_MENU_TITLES.toArray(new String[0]); } + public static String advancedEntryTitle() { + return "高级与调试"; + } + public static String[] projectQuickActions() { return PROJECT_QUICK_ACTIONS.toArray(new String[0]); } diff --git a/android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java b/android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java index 2c8d622..7710b1c 100644 --- a/android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java +++ b/android/app/src/test/java/com/hyzq/boss/WechatSurfaceMapperTest.java @@ -77,6 +77,11 @@ public class WechatSurfaceMapperTest { ); } + @Test + public void advancedEntryTitle_movesOpsOutOfMainMePage() throws Exception { + assertEquals("高级与调试", WechatSurfaceMapper.advancedEntryTitle()); + } + @Test public void projectQuickActions_keepOnlyGoalsAndVersions() throws Exception { assertArrayEquals(