diff --git a/README.md b/README.md index 1941d69..72c836b 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ Android APK: - 已生成 Android debug APK:`android/app/build/outputs/apk/debug/app-debug.apk` - 已生成 Android signed release APK:`android/app/build/outputs/apk/release/app-release.apk` - `npm run apk:release` 还会额外产出带版本号的文件:`android/app/build/outputs/apk/release/boss-android-v{versionName}-release.apk` -- 当前最新 release 构建版本:`2.5.0`(`versionCode=13`) +- 当前最新 release 构建版本:`2.5.1`(`versionCode=14`) - 当前 APK 已切到原生 Android 客户端:`MainActivity + BossApiClient + 原生 XML 布局` - 当前原生活动页已经覆盖:会话首页、项目详情、项目目标、版本记录、会话信息、群资料、发起群聊、消息转发、线程详情、设备详情、添加设备、账号与安全、设置、AI 账号、技能、运维中心、关于 - 当前原生一级体验已回退到微信式交互:`会话 / 设备 / 我的` 固定底部 tab,会话首页是简单聊天列表,`主 Agent / 审计对话` 以普通置顶会话样式排在最前;项目详情页是聊天优先,只保留 `项目目标 / 版本记录` 两个轻入口 @@ -110,6 +110,7 @@ Android APK: - `2.3.0` 已把会话模型切到“线程 = 聊天窗口”,补上文件夹名副信息、后台活跃数量动态图标、微信式会话信息页、线程改名、独立群聊创建、群资料页,以及 `主 Agent / 审计对话` 普通置顶会话化 - `2.4.0` 已把消息转发切到微信式原生链路:聊天页支持长按消息操作、多选合并转发、统一目标会话选择页;单条消息转发显示为普通转发消息,多条消息转发显示为“聊天记录”卡片 - `2.5.0` 已补齐聊天附件主链:原生聊天框左侧 `+` 会打开底部抽屉,支持图片 / 视频 / 文件发送;默认走服务器文件存储,`我的 > 附件与存储` 可切到阿里 OSS 私有桶;附件消息已支持下载 / 打开、手动分析、自动分析状态,以及带 task token 的主 Agent 附件分析链接 +- `2.5.1` 继续收口微信式原生 UI:聊天页普通态顶部已隐藏刷新按钮,只保留右上角“信息”;发起群聊页顶部说明和选择区已压成更轻的会话式密度,候选线程继续复用微信式会话卡片 ## 本地启动 diff --git a/android/app/build.gradle b/android/app/build.gradle index 0c02155..92695ab 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -36,8 +36,8 @@ android { applicationId "com.hyzq.boss" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 13 - versionName "2.5.0" + versionCode 14 + versionName "2.5.1" buildConfigField "String", "BOSS_API_BASE_URL", "\"https://boss.hyzq.net\"" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/android/app/src/main/java/com/hyzq/boss/GroupCreateActivity.java b/android/app/src/main/java/com/hyzq/boss/GroupCreateActivity.java index 43337bd..f87e217 100644 --- a/android/app/src/main/java/com/hyzq/boss/GroupCreateActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/GroupCreateActivity.java @@ -84,27 +84,15 @@ public class GroupCreateActivity extends BossScreenActivity { sourceProjectName = threadMeta == null ? sourceProjectName : threadMeta.optString("threadDisplayName", sourceProjectName == null ? "当前会话" : sourceProjectName); - - appendContent(BossUi.buildCard( - this, - "新建独立群聊", - "群聊不是升级原会话,而是以当前会话为源,新建一个独立线程。", - buildSourceMeta(threadMeta, participants) - )); - - appendContent(BossUi.buildCard( - this, - sourceProjectName, - buildSourceBody(threadMeta, participants), - sourceProjectId + (sourceFolderName.isEmpty() ? "" : " · " + sourceFolderName) - )); - } else { - appendContent(BossUi.buildCard( - this, - "从会话首页发起群聊", - "你可以直接把任意线程拉进一个新的独立群聊,原来的单线程会话会保留不变。", - "至少选择 2 个线程" - )); + } + for (SummaryCardSpec cardSpec : buildHeaderCardSpecs( + hasSourceProject(), + sourceProjectId, + sourceProjectName, + threadMeta, + participants + )) { + appendContent(BossUi.buildCard(this, cardSpec.title, cardSpec.body, cardSpec.meta)); } if (rebuildCandidates) { @@ -133,15 +121,16 @@ public class GroupCreateActivity extends BossScreenActivity { lastCandidateProjectIds.addAll(nextCandidateProjectIds); } + SummaryCardSpec selectionCard = buildSelectionCardSpec( + candidates.size(), + selectedProjectIds.size(), + hasSourceProject() + ); appendContent(BossUi.buildCard( this, - "选择其他线程", - candidates.isEmpty() - ? "当前没有可加入的其他线程。" - : selectedProjectIds.isEmpty() - ? "你已取消全部勾选,可继续手动选择。" - : "已保留你当前的勾选状态。", - "已选 " + selectedProjectIds.size() + " 个线程" + selectionCard.title, + selectionCard.body, + selectionCard.meta )); candidateListLayout = new LinearLayout(this); @@ -203,6 +192,59 @@ public class GroupCreateActivity extends BossScreenActivity { ); } + static List buildHeaderCardSpecs( + boolean hasSourceProject, + @Nullable String sourceProjectId, + @Nullable String sourceProjectName, + @Nullable JSONObject threadMeta, + @Nullable JSONArray participants + ) { + List cards = new ArrayList<>(1); + if (hasSourceProject) { + String resolvedSourceProjectName = sourceProjectName == null || sourceProjectName.isEmpty() + ? "当前会话" + : sourceProjectName; + cards.add(new SummaryCardSpec( + resolvedSourceProjectName, + buildSourceBody(sourceProjectId, threadMeta, participants), + "新群会单独创建,原会话保留" + )); + return cards; + } + cards.add(new SummaryCardSpec( + "从会话中发起群聊", + "至少选择 2 个线程,新群会单独创建。", + "原有单线程会话会保留" + )); + return cards; + } + + static SummaryCardSpec buildSelectionCardSpec( + int candidateCount, + int selectedCount, + boolean hasSourceProject + ) { + if (candidateCount <= 0) { + return new SummaryCardSpec( + "选择其他线程", + "当前没有可加入的其他线程", + "候选 0 个 · 已选 0 个" + ); + } + if (selectedCount <= 0) { + return new SummaryCardSpec( + "选择其他线程", + hasSourceProject ? "至少选择 1 个其他线程" : "至少选择 2 个线程", + "候选 " + candidateCount + " 个 · 已选 0 个" + ); + } + return new SummaryCardSpec( + "选择其他线程", + "继续点选可调整成员", + "候选 " + candidateCount + " 个 · 已选 " + selectedCount + " 个" + ); + } + private LinearLayout buildCandidateRow(CandidateConversation candidate) { return BossUi.buildConversationRow( this, @@ -348,25 +390,33 @@ public class GroupCreateActivity extends BossScreenActivity { return sourceProjectId != null && !sourceProjectId.isEmpty(); } - private String buildSourceMeta(@Nullable JSONObject threadMeta, @Nullable JSONArray participants) { - String folderName = threadMeta == null ? "" : threadMeta.optString("folderName", ""); - int count = participants == null ? 0 : participants.length(); - String memberLabel = count <= 0 ? "暂无参与线程" : count + " 个参与线程"; - if (folderName.isEmpty()) { - return memberLabel; - } - return folderName + " · " + memberLabel; - } - - private String buildSourceBody(@Nullable JSONObject threadMeta, @Nullable JSONArray participants) { + private static String buildSourceBody( + @Nullable String sourceProjectId, + @Nullable JSONObject threadMeta, + @Nullable JSONArray participants + ) { String threadId = threadMeta == null ? sourceProjectId : threadMeta.optString("threadId", sourceProjectId); String folderName = threadMeta == null ? "" : threadMeta.optString("folderName", ""); - StringBuilder builder = new StringBuilder(); - builder.append("来源线程:").append(threadId); - builder.append("\n文件夹:").append(folderName.isEmpty() ? "未命名文件夹" : folderName); - builder.append("\n参与线程:").append(participants == null ? 0 : participants.length()); - builder.append("\n默认规则:会自动勾选当前会话之外的其他线程"); - return builder.toString(); + String resolvedFolderName = folderName.isEmpty() ? "未命名文件夹" : folderName; + String resolvedThreadId = threadId == null || threadId.isEmpty() ? "未命名线程" : threadId; + return "来源线程 " + resolvedThreadId + + " · " + + resolvedFolderName + + " · " + + (participants == null ? 0 : participants.length()) + + " 个参与线程"; + } + + static final class SummaryCardSpec { + final String title; + final String body; + final String meta; + + SummaryCardSpec(String title, String body, String meta) { + this.title = title; + this.body = body; + this.meta = meta; + } } private static final class CandidateConversation { diff --git a/android/app/src/main/java/com/hyzq/boss/ProjectChatUiState.java b/android/app/src/main/java/com/hyzq/boss/ProjectChatUiState.java index cebfeb2..efa34a6 100644 --- a/android/app/src/main/java/com/hyzq/boss/ProjectChatUiState.java +++ b/android/app/src/main/java/com/hyzq/boss/ProjectChatUiState.java @@ -136,7 +136,7 @@ public final class ProjectChatUiState { false, true, false, - true, + !conversationInfoReady, conversationInfoReady, false, "返回", diff --git a/android/app/src/test/java/com/hyzq/boss/GroupCreateActivityTest.java b/android/app/src/test/java/com/hyzq/boss/GroupCreateActivityTest.java index 509c954..849b49e 100644 --- a/android/app/src/test/java/com/hyzq/boss/GroupCreateActivityTest.java +++ b/android/app/src/test/java/com/hyzq/boss/GroupCreateActivityTest.java @@ -4,6 +4,7 @@ import org.json.JSONArray; import org.json.JSONObject; import org.junit.Test; +import java.util.List; import java.util.LinkedHashSet; import java.util.Set; @@ -12,6 +13,41 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class GroupCreateActivityTest { + @Test + public void buildHeaderCardSpecs_collapsesSourceFlowIntoSingleCompactCard() { + JSONObject threadMeta = new StubJSONObject() + .withString("threadDisplayName", "北区试产线回归") + .withString("threadId", "thread-7") + .withString("folderName", "Mac Studio"); + JSONArray participants = new StubJSONArray( + new StubJSONObject(), + new StubJSONObject(), + new StubJSONObject() + ); + + List cards = GroupCreateActivity.buildHeaderCardSpecs( + true, + "source-1", + "北区试产线回归", + threadMeta, + participants + ); + + assertEquals(1, cards.size()); + assertEquals("北区试产线回归", cards.get(0).title); + assertEquals("来源线程 thread-7 · Mac Studio · 3 个参与线程", cards.get(0).body); + assertEquals("新群会单独创建,原会话保留", cards.get(0).meta); + } + + @Test + public void buildSelectionCardSpec_usesCompactWechatStyleHint() { + GroupCreateActivity.SummaryCardSpec card = GroupCreateActivity.buildSelectionCardSpec(5, 0, true); + + assertEquals("选择其他线程", card.title); + assertEquals("至少选择 1 个其他线程", card.body); + assertEquals("候选 5 个 · 已选 0 个", card.meta); + } + @Test public void collectSelectableConversationItems_filtersOutExistingGroupChats() { JSONObject threadConversation = new StubJSONObject() diff --git a/android/app/src/test/java/com/hyzq/boss/GroupCreateActivityUiTest.java b/android/app/src/test/java/com/hyzq/boss/GroupCreateActivityUiTest.java new file mode 100644 index 0000000..03a7a4b --- /dev/null +++ b/android/app/src/test/java/com/hyzq/boss/GroupCreateActivityUiTest.java @@ -0,0 +1,142 @@ +package com.hyzq.boss; + +import static org.junit.Assert.assertTrue; + +import android.content.Intent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = 34) +public class GroupCreateActivityUiTest { + @Test + public void renderCreatePageUsesCompactSummaryCardsForSourceFlow() throws Exception { + Intent intent = new Intent() + .putExtra(GroupCreateActivity.EXTRA_SOURCE_PROJECT_ID, "source-1") + .putExtra(GroupCreateActivity.EXTRA_SOURCE_PROJECT_NAME, "北区试产线回归"); + TestGroupCreateActivity activity = Robolectric + .buildActivity(TestGroupCreateActivity.class, intent) + .setup() + .get(); + + ReflectionHelpers.callInstanceMethod( + activity, + "renderCreatePage", + ReflectionHelpers.ClassParameter.from(JSONObject.class, buildParticipantsPayload()), + ReflectionHelpers.ClassParameter.from(JSONObject.class, buildConversationsPayload()), + ReflectionHelpers.ClassParameter.from(boolean.class, true) + ); + + LinearLayout content = activity.findViewById(R.id.screen_content); + assertTrue(viewTreeContainsText(content.getChildAt(0), "北区试产线回归")); + assertTrue(viewTreeContainsText(content.getChildAt(0), "来源线程 thread-7 · Mac Studio · 3 个参与线程")); + assertTrue(viewTreeContainsText(content.getChildAt(0), "新群会单独创建,原会话保留")); + assertTrue(viewTreeContainsText(content.getChildAt(1), "选择其他线程")); + assertTrue(viewTreeContainsText(content.getChildAt(1), "继续点选可调整成员")); + assertTrue(viewTreeContainsText(content.getChildAt(1), "候选 2 个 · 已选 2 个")); + } + + @Test + public void toggleSelectionRefreshesRenderedSelectionSummary() throws Exception { + Intent intent = new Intent() + .putExtra(GroupCreateActivity.EXTRA_SOURCE_PROJECT_ID, "source-1") + .putExtra(GroupCreateActivity.EXTRA_SOURCE_PROJECT_NAME, "北区试产线回归"); + TestGroupCreateActivity activity = Robolectric + .buildActivity(TestGroupCreateActivity.class, intent) + .setup() + .get(); + + ReflectionHelpers.callInstanceMethod( + activity, + "renderCreatePage", + ReflectionHelpers.ClassParameter.from(JSONObject.class, buildParticipantsPayload()), + ReflectionHelpers.ClassParameter.from(JSONObject.class, buildConversationsPayload()), + ReflectionHelpers.ClassParameter.from(boolean.class, true) + ); + + ReflectionHelpers.callInstanceMethod( + activity, + "toggleSelection", + ReflectionHelpers.ClassParameter.from(String.class, "thread-2") + ); + ReflectionHelpers.callInstanceMethod( + activity, + "toggleSelection", + ReflectionHelpers.ClassParameter.from(String.class, "thread-3") + ); + + LinearLayout content = activity.findViewById(R.id.screen_content); + assertTrue(viewTreeContainsText(content.getChildAt(1), "至少选择 1 个其他线程")); + assertTrue(viewTreeContainsText(content.getChildAt(1), "候选 2 个 · 已选 0 个")); + } + + private static JSONObject buildParticipantsPayload() throws Exception { + JSONObject threadMeta = new JSONObject() + .put("threadDisplayName", "北区试产线回归") + .put("threadId", "thread-7") + .put("folderName", "Mac Studio"); + JSONArray participants = new JSONArray() + .put(new JSONObject().put("projectId", "source-1")) + .put(new JSONObject().put("projectId", "thread-2")) + .put(new JSONObject().put("projectId", "thread-3")); + return new JSONObject() + .put("threadMeta", threadMeta) + .put("participants", participants); + } + + private static JSONObject buildConversationsPayload() throws Exception { + JSONArray conversations = new JSONArray() + .put(new JSONObject() + .put("projectId", "thread-2") + .put("projectTitle", "硬件审计协作") + .put("folderLabel", "Mac Studio") + .put("lastMessagePreview", "检查摄像头供电链路") + .put("latestReplyLabel", "09:28") + .put("isGroup", false)) + .put(new JSONObject() + .put("projectId", "thread-3") + .put("projectTitle", "Boss 移动控制台") + .put("folderLabel", "Boss") + .put("lastMessagePreview", "统一顶部按钮样式") + .put("latestReplyLabel", "09:31") + .put("isGroup", false)); + return new JSONObject().put("conversations", conversations); + } + + private static boolean viewTreeContainsText(View root, String expectedText) { + if (root instanceof TextView) { + CharSequence text = ((TextView) root).getText(); + if (expectedText.contentEquals(text)) { + return true; + } + } + if (!(root instanceof ViewGroup)) { + return false; + } + ViewGroup group = (ViewGroup) root; + for (int index = 0; index < group.getChildCount(); index += 1) { + if (viewTreeContainsText(group.getChildAt(index), expectedText)) { + return true; + } + } + return false; + } + + public static class TestGroupCreateActivity extends GroupCreateActivity { + @Override + protected void reload() { + // Tests drive renderCreatePage manually to avoid network work. + } + } +} diff --git a/android/app/src/test/java/com/hyzq/boss/ProjectChatUiStateTest.java b/android/app/src/test/java/com/hyzq/boss/ProjectChatUiStateTest.java index 36020c5..18122d6 100644 --- a/android/app/src/test/java/com/hyzq/boss/ProjectChatUiStateTest.java +++ b/android/app/src/test/java/com/hyzq/boss/ProjectChatUiStateTest.java @@ -111,7 +111,7 @@ public class ProjectChatUiStateTest { assertFalse(chromeState.multiSelecting); assertTrue(chromeState.showComposer); assertFalse(chromeState.showMultiSelectBar); - assertTrue(chromeState.showRefresh); + assertFalse(chromeState.showRefresh); assertTrue(chromeState.showHeaderAction); assertFalse(chromeState.forwardEnabled); assertEquals("返回", chromeState.backLabel); @@ -119,6 +119,22 @@ public class ProjectChatUiStateTest { assertEquals("归档确认", chromeState.subtitle); } + @Test + public void chromeStateRestoresRefreshBeforeConversationInfoIsReady() { + ProjectChatUiState.ChromeState chromeState = + ProjectChatUiState.resolveChromeState(ProjectChatUiState.emptySelection(), false, "北区试产线回归", "归档确认"); + + assertFalse(chromeState.multiSelecting); + assertTrue(chromeState.showComposer); + assertFalse(chromeState.showMultiSelectBar); + assertTrue(chromeState.showRefresh); + assertFalse(chromeState.showHeaderAction); + assertFalse(chromeState.forwardEnabled); + assertEquals("返回", chromeState.backLabel); + assertEquals("北区试产线回归", chromeState.title); + assertEquals("归档确认", chromeState.subtitle); + } + @Test public void reconcileSelectionDropsMessagesMissingFromRenderSet() { ProjectChatUiState.SelectionState state = ProjectChatUiState.toggleSelection(null, "m1"); diff --git a/android/app/src/test/java/com/hyzq/boss/ProjectDetailActivityChromeBindingsTest.java b/android/app/src/test/java/com/hyzq/boss/ProjectDetailActivityChromeBindingsTest.java index d20d18d..7652898 100644 --- a/android/app/src/test/java/com/hyzq/boss/ProjectDetailActivityChromeBindingsTest.java +++ b/android/app/src/test/java/com/hyzq/boss/ProjectDetailActivityChromeBindingsTest.java @@ -46,7 +46,7 @@ public class ProjectDetailActivityChromeBindingsTest { assertFalse(bindings.multiSelecting); assertTrue(bindings.showComposer); assertFalse(bindings.showMultiSelectBar); - assertTrue(bindings.showRefresh); + assertFalse(bindings.showRefresh); assertTrue(bindings.showHeaderAction); assertFalse(bindings.enableForwardButton); assertTrue(bindings.enablePullRefresh); diff --git a/android/app/src/test/java/com/hyzq/boss/ProjectDetailActivityUiTest.java b/android/app/src/test/java/com/hyzq/boss/ProjectDetailActivityUiTest.java index 878d1c3..04aeccf 100644 --- a/android/app/src/test/java/com/hyzq/boss/ProjectDetailActivityUiTest.java +++ b/android/app/src/test/java/com/hyzq/boss/ProjectDetailActivityUiTest.java @@ -75,7 +75,7 @@ public class ProjectDetailActivityUiTest { assertEquals(View.VISIBLE, composerRow.getVisibility()); assertEquals(View.GONE, multiSelectActions.getVisibility()); assertEquals("返回", backButton.getText().toString()); - assertEquals(View.VISIBLE, refreshButton.getVisibility()); + assertEquals(View.GONE, refreshButton.getVisibility()); } @Test diff --git a/docs/architecture/ai_handoff_index_cn.md b/docs/architecture/ai_handoff_index_cn.md index aa696d1..e7899d6 100644 --- a/docs/architecture/ai_handoff_index_cn.md +++ b/docs/architecture/ai_handoff_index_cn.md @@ -153,7 +153,7 @@ - 邮件:`Postfix + Dovecot` - Android:`AppCompatActivity + 原生 XML 布局 + HttpURLConnection` - 原生登录恢复:`SharedPreferences + restore token` -- 当前最新原生 APK:`2.5.0`(`versionCode=13`) +- 当前最新原生 APK:`2.5.1`(`versionCode=14`) 当前不要误判成已经用了: diff --git a/docs/architecture/current_runtime_and_deploy_status_cn.md b/docs/architecture/current_runtime_and_deploy_status_cn.md index 1ec8acb..aed3629 100644 --- a/docs/architecture/current_runtime_and_deploy_status_cn.md +++ b/docs/architecture/current_runtime_and_deploy_status_cn.md @@ -116,7 +116,7 @@ cd /Users/kris/code/boss - 当前已生成 Android debug APK:`android/app/build/outputs/apk/debug/app-debug.apk` - 当前已生成 Android signed release APK:`android/app/build/outputs/apk/release/app-release.apk` - 当前 release 构建还会额外生成带版本号的 APK:`android/app/build/outputs/apk/release/boss-android-v{versionName}-release.apk` -- 当前最新 release 构建版本:`2.5.0`(`versionCode=13`) +- 当前最新 release 构建版本:`2.5.1`(`versionCode=14`) - 当前 release keystore 位于本机 `android/keystores/boss-release.keystore`,签名参数位于 `android/signing/release-signing.properties` - `2.0.1` 已在本机连接的华为真机上复核通过,修复了 `Theme.SplashScreen` 导致的 `AppCompatActivity` 启动闪退 - `2.1.0` 已把 Web 一级页和主要二级页全部补成原生活动页:`MainActivity / ProjectDetailActivity / ProjectGoalsActivity / ProjectVersionsActivity / ProjectForwardActivity / ThreadDetailActivity / DeviceDetailActivity / DeviceEnrollmentActivity / SkillInventoryActivity / SecurityActivity / SettingsActivity / AiAccountsActivity / OpsCenterActivity / AboutActivity` @@ -128,6 +128,8 @@ cd /Users/kris/code/boss - `2.4.0` 已把原生消息转发切到微信式链路:单条消息支持长按直接转发,多选消息支持合并转发成聊天记录卡片,统一使用原生会话选择页替换旧的备注转发页 - `2.5.0` 已补齐聊天附件主链:原生聊天框左侧 `+` 已改成底部抽屉,支持图片 / 视频 / 文件发送;图片 / PDF / 文本会自动排队给主 Agent 分析,视频 / Office / 大文件改成手动触发 - `2.5.0` 已上线 `我的 > 附件与存储`:默认使用服务器文件存储,用户可切到阿里 OSS 私有桶并填写最小配置;下载链会使用附件上传时固化的 OSS 快照,避免后续改配置后旧附件失效 +- `2.5.1` 已继续回退聊天详情页顶部交互:普通聊天态不再显示“刷新”,只保留微信式右上角“信息”入口;多选态的“取消 / 转发”保持不变 +- `2.5.1` 已压缩“发起群聊”页首信息密度:来源会话场景只保留一张紧凑摘要卡,选择区改成更短的微信式提示,同时保留会话卡片式候选列表 - 当前附件分析任务已带受控 `task token` 下载链接和文本摘录:本地开发环境会跟随请求 origin 生成链接,生产环境默认走 `https://boss.hyzq.net` - `2.5.x` 当前已补上会话首页独立建群入口:可以不从单线程聊天内部出发,直接在会话首页右上角 `+` 建立新群聊;同时已把多个原生自定义 top bar 页面统一纳入状态栏安全区处理 diff --git a/public/downloads/boss-android-latest-aab.json b/public/downloads/boss-android-latest-aab.json index ffee918..2116dcf 100644 --- a/public/downloads/boss-android-latest-aab.json +++ b/public/downloads/boss-android-latest-aab.json @@ -1,11 +1,11 @@ { "artifactType": "aab", - "fileName": "boss-android-v2.5.0-release.aab", - "urlPath": "/downloads/boss-android-v2.5.0-release.aab", - "sizeBytes": 2906325, - "updatedAt": "2026-03-29T09:19:37Z", - "sha256": "e230a1e0eae8e1e6d264f11feb3125ff40661dc2e049e18d8f683c3571e3a568", - "versionName": "2.5.0", - "versionCode": 13, + "fileName": "boss-android-v2.5.1-release.aab", + "urlPath": "/downloads/boss-android-v2.5.1-release.aab", + "sizeBytes": 2910555, + "updatedAt": "2026-03-29T11:58:09Z", + "sha256": "a1db186f0bd8ac9439f9cd7b9116d273ddaa2d169eb46613a29b848b1824458b", + "versionName": "2.5.1", + "versionCode": 14, "buildFlavor": "release" } diff --git a/public/downloads/boss-android-latest.aab b/public/downloads/boss-android-latest.aab index 9498c1b..761e957 100644 Binary files a/public/downloads/boss-android-latest.aab and b/public/downloads/boss-android-latest.aab differ diff --git a/public/downloads/boss-android-latest.apk b/public/downloads/boss-android-latest.apk index 143d82a..0fa1dcd 100644 Binary files a/public/downloads/boss-android-latest.apk and b/public/downloads/boss-android-latest.apk differ diff --git a/public/downloads/boss-android-latest.json b/public/downloads/boss-android-latest.json index 75a7da6..02738a0 100644 --- a/public/downloads/boss-android-latest.json +++ b/public/downloads/boss-android-latest.json @@ -1,10 +1,10 @@ { - "fileName": "boss-android-v2.5.0-release.apk", + "fileName": "boss-android-v2.5.1-release.apk", "urlPath": "/api/v1/user/ota/package", - "sizeBytes": 3083087, - "updatedAt": "2026-03-29T09:20:05Z", - "sha256": "92183a0ebed80da5e363ffcd8e41a4acfd650aac76072ac5a4e432af5902f59f", - "versionName": "2.5.0", - "versionCode": 13, + "sizeBytes": 3087319, + "updatedAt": "2026-03-29T11:58:01Z", + "sha256": "c97c17d2989066027a68f291c830d007696403c9f484fc4278310d8303cb8d99", + "versionName": "2.5.1", + "versionCode": 14, "buildFlavor": "release" } diff --git a/public/downloads/boss-android-v2.5.1-release.aab b/public/downloads/boss-android-v2.5.1-release.aab new file mode 100644 index 0000000..761e957 Binary files /dev/null and b/public/downloads/boss-android-v2.5.1-release.aab differ diff --git a/public/downloads/boss-android-v2.5.1-release.apk b/public/downloads/boss-android-v2.5.1-release.apk new file mode 100644 index 0000000..0fa1dcd Binary files /dev/null and b/public/downloads/boss-android-v2.5.1-release.apk differ