diff --git a/android/app/src/main/java/com/hyzq/boss/ConversationFolderActivity.java b/android/app/src/main/java/com/hyzq/boss/ConversationFolderActivity.java index e89ae57..2702a9f 100644 --- a/android/app/src/main/java/com/hyzq/boss/ConversationFolderActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/ConversationFolderActivity.java @@ -21,6 +21,8 @@ public class ConversationFolderActivity extends BossScreenActivity { folderKey = getIntent().getStringExtra(EXTRA_FOLDER_KEY); folderName = getIntent().getStringExtra(EXTRA_FOLDER_NAME); configureScreen(folderName == null || folderName.isEmpty() ? "项目线程" : folderName, "同一项目下的线程列表"); + refreshButton.setVisibility(android.view.View.GONE); + setHeaderAction("...", v -> showMoreMenu()); reload(); } @@ -99,4 +101,10 @@ public class ConversationFolderActivity extends BossScreenActivity { intent.putExtra(ProjectDetailActivity.EXTRA_PROJECT_NAME, projectName); startActivity(intent); } + + private void showMoreMenu() { + new androidx.appcompat.app.AlertDialog.Builder(this) + .setItems(new CharSequence[]{"刷新"}, (dialog, which) -> reload()) + .show(); + } } diff --git a/android/app/src/main/java/com/hyzq/boss/ConversationInfoActivity.java b/android/app/src/main/java/com/hyzq/boss/ConversationInfoActivity.java index 4bc7dd0..580af67 100644 --- a/android/app/src/main/java/com/hyzq/boss/ConversationInfoActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/ConversationInfoActivity.java @@ -31,7 +31,8 @@ public class ConversationInfoActivity extends BossScreenActivity { projectId = getIntent().getStringExtra(EXTRA_PROJECT_ID); projectName = getIntent().getStringExtra(EXTRA_PROJECT_NAME); configureScreen("会话信息", projectName == null ? "单线程会话" : projectName); - setHeaderAction("改名", v -> openRenameDialog()); + refreshButton.setVisibility(android.view.View.GONE); + setHeaderAction("...", v -> showMoreMenu()); reload(); } @@ -185,6 +186,18 @@ public class ConversationInfoActivity extends BossScreenActivity { .show(); } + private void showMoreMenu() { + new AlertDialog.Builder(this) + .setItems(new CharSequence[]{"改名", "刷新"}, (dialog, which) -> { + if (which == 0) { + openRenameDialog(); + return; + } + reload(); + }) + .show(); + } + private void saveConversationName(String name) { if (name.isEmpty()) { showMessage("线程名不能为空"); diff --git a/android/app/src/main/java/com/hyzq/boss/GroupInfoActivity.java b/android/app/src/main/java/com/hyzq/boss/GroupInfoActivity.java index 918f9ee..a2c62ba 100644 --- a/android/app/src/main/java/com/hyzq/boss/GroupInfoActivity.java +++ b/android/app/src/main/java/com/hyzq/boss/GroupInfoActivity.java @@ -30,7 +30,8 @@ public class GroupInfoActivity extends BossScreenActivity { projectId = getIntent().getStringExtra(EXTRA_PROJECT_ID); projectName = getIntent().getStringExtra(EXTRA_PROJECT_NAME); configureScreen("群资料", projectName == null ? "协作群聊" : projectName); - setHeaderAction("改名", v -> openRenameDialog()); + refreshButton.setVisibility(android.view.View.GONE); + setHeaderAction("...", v -> showMoreMenu()); reload(); } @@ -425,6 +426,18 @@ public class GroupInfoActivity extends BossScreenActivity { }); } + private void showMoreMenu() { + new AlertDialog.Builder(this) + .setItems(new CharSequence[]{"改名", "刷新"}, (dialog, which) -> { + if (which == 0) { + openRenameDialog(); + return; + } + reload(); + }) + .show(); + } + private String resolveBackendLabel(JSONObject backendPayload, String backendId) { JSONArray availableChoices = backendPayload.optJSONArray("availableChoices"); if (availableChoices != null) { diff --git a/android/app/src/test/java/com/hyzq/boss/ConversationFolderActivityTest.java b/android/app/src/test/java/com/hyzq/boss/ConversationFolderActivityTest.java new file mode 100644 index 0000000..e923a33 --- /dev/null +++ b/android/app/src/test/java/com/hyzq/boss/ConversationFolderActivityTest.java @@ -0,0 +1,108 @@ +package com.hyzq.boss; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.content.Intent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; + +import androidx.appcompat.app.AlertDialog; + +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.shadows.ShadowDialog; +import org.robolectric.util.ReflectionHelpers; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = 34) +public class ConversationFolderActivityTest { + @Test + public void conversationFolderUsesOverflowMenuInTopBar() throws Exception { + Intent intent = new Intent() + .putExtra(ConversationFolderActivity.EXTRA_FOLDER_KEY, "talking") + .putExtra(ConversationFolderActivity.EXTRA_FOLDER_NAME, "Talking"); + TestConversationFolderActivity activity = Robolectric + .buildActivity(TestConversationFolderActivity.class, intent) + .setup() + .get(); + + ReflectionHelpers.callInstanceMethod( + activity, + "renderFolder", + ReflectionHelpers.ClassParameter.from(JSONObject.class, buildFolderPayload()) + ); + + Button headerAction = activity.findViewById(R.id.screen_header_action); + Button refreshButton = activity.findViewById(R.id.screen_refresh_button); + LinearLayout content = activity.findViewById(R.id.screen_content); + + assertEquals("...", headerAction.getText().toString()); + assertEquals(View.GONE, refreshButton.getVisibility()); + assertTrue(viewTreeContainsText(content, "Talking")); + assertTrue(viewTreeContainsText(content, "查询腾讯HAI GPU服务器")); + + ReflectionHelpers.callInstanceMethod(activity, "showMoreMenu"); + + android.app.Dialog latestDialog = ShadowDialog.getLatestDialog(); + assertTrue(latestDialog instanceof AlertDialog); + ListView listView = ((AlertDialog) latestDialog).getListView(); + assertTrue(viewTreeContainsText(listView.getAdapter().getView(0, null, listView), "刷新")); + } + + private static JSONObject buildFolderPayload() throws Exception { + JSONArray threads = new JSONArray() + .put(new JSONObject() + .put("projectId", "project-1") + .put("threadTitle", "查询腾讯HAI GPU服务器") + .put("folderLabel", "Talking") + .put("lastMessagePreview", "已从设备导入线程") + .put("timeLabel", "02:28")) + .put(new JSONObject() + .put("projectId", "project-2") + .put("threadTitle", "状态栏显示CPU和内存") + .put("folderLabel", "Talking") + .put("lastMessagePreview", "已从设备导入线程") + .put("timeLabel", "02:12")); + return new JSONObject() + .put("folderLabel", "Talking") + .put("deviceName", "Mac Studio") + .put("threadCount", 2) + .put("threads", threads); + } + + 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 TestConversationFolderActivity extends ConversationFolderActivity { + @Override + protected void reload() { + // Tests render the folder state directly. + } + } +} diff --git a/android/app/src/test/java/com/hyzq/boss/ConversationInfoActivityTest.java b/android/app/src/test/java/com/hyzq/boss/ConversationInfoActivityTest.java index 61eafe4..fb6c04c 100644 --- a/android/app/src/test/java/com/hyzq/boss/ConversationInfoActivityTest.java +++ b/android/app/src/test/java/com/hyzq/boss/ConversationInfoActivityTest.java @@ -8,9 +8,13 @@ import static org.junit.Assert.assertTrue; import android.content.Intent; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; import android.widget.LinearLayout; +import android.widget.ListView; import android.widget.TextView; +import androidx.appcompat.app.AlertDialog; + import org.json.JSONArray; import org.json.JSONObject; import org.junit.Test; @@ -19,6 +23,7 @@ import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.Shadows; import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowDialog; import org.robolectric.util.ReflectionHelpers; @RunWith(RobolectricTestRunner.class) @@ -95,6 +100,31 @@ public class ConversationInfoActivityTest { ); } + @Test + public void conversationInfoUsesOverflowMenuInTopBar() { + Intent intent = new Intent() + .putExtra(ConversationInfoActivity.EXTRA_PROJECT_ID, "project-1") + .putExtra(ConversationInfoActivity.EXTRA_PROJECT_NAME, "北区试产线回归"); + TestConversationInfoActivity activity = Robolectric + .buildActivity(TestConversationInfoActivity.class, intent) + .setup() + .get(); + + Button headerAction = activity.findViewById(R.id.screen_header_action); + Button refreshButton = activity.findViewById(R.id.screen_refresh_button); + + assertEquals("...", headerAction.getText().toString()); + assertEquals(View.GONE, refreshButton.getVisibility()); + + ReflectionHelpers.callInstanceMethod(activity, "showMoreMenu"); + + android.app.Dialog latestDialog = ShadowDialog.getLatestDialog(); + assertTrue(latestDialog instanceof AlertDialog); + ListView listView = ((AlertDialog) latestDialog).getListView(); + assertTrue(viewTreeContainsText(listView.getAdapter().getView(0, null, listView), "改名")); + assertTrue(viewTreeContainsText(listView.getAdapter().getView(1, null, listView), "刷新")); + } + private static JSONObject buildDetailPayload() throws Exception { JSONObject threadMeta = new JSONObject() .put("threadId", "thread-7") diff --git a/android/app/src/test/java/com/hyzq/boss/GroupInfoActivityTest.java b/android/app/src/test/java/com/hyzq/boss/GroupInfoActivityTest.java index 665fecf..199fa51 100644 --- a/android/app/src/test/java/com/hyzq/boss/GroupInfoActivityTest.java +++ b/android/app/src/test/java/com/hyzq/boss/GroupInfoActivityTest.java @@ -9,9 +9,13 @@ import android.content.Intent; import android.os.Looper; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; import android.widget.LinearLayout; +import android.widget.ListView; import android.widget.TextView; +import androidx.appcompat.app.AlertDialog; + import org.json.JSONArray; import org.json.JSONObject; import org.junit.Test; @@ -20,6 +24,7 @@ import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.Shadows; import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowDialog; import org.robolectric.util.ReflectionHelpers; import java.io.ByteArrayInputStream; @@ -179,6 +184,31 @@ public class GroupInfoActivityTest { assertTrue(viewTreeContainsText(content, "OMX Team Runtime 当前不可用,当前已自动回退到 Boss Native Orchestrator。")); } + @Test + public void groupInfoUsesOverflowMenuInTopBar() { + Intent intent = new Intent() + .putExtra(GroupInfoActivity.EXTRA_PROJECT_ID, "group-1") + .putExtra(GroupInfoActivity.EXTRA_PROJECT_NAME, "巡检协作群"); + TestGroupInfoActivity activity = Robolectric + .buildActivity(TestGroupInfoActivity.class, intent) + .setup() + .get(); + + Button headerAction = activity.findViewById(R.id.screen_header_action); + Button refreshButton = activity.findViewById(R.id.screen_refresh_button); + + assertEquals("...", headerAction.getText().toString()); + assertEquals(View.GONE, refreshButton.getVisibility()); + + ReflectionHelpers.callInstanceMethod(activity, "showMoreMenu"); + + android.app.Dialog latestDialog = ShadowDialog.getLatestDialog(); + assertTrue(latestDialog instanceof AlertDialog); + ListView listView = ((AlertDialog) latestDialog).getListView(); + assertTrue(viewTreeContainsText(listView.getAdapter().getView(0, null, listView), "改名")); + assertTrue(viewTreeContainsText(listView.getAdapter().getView(1, null, listView), "刷新")); + } + @Test public void saveOrchestrationBackendUsesScopedEndpoint() throws Exception { Intent intent = new Intent()