Fix conversation folder search targeting
This commit is contained in:
@@ -681,6 +681,17 @@ public final class BossUi {
|
||||
boolean selectionMode,
|
||||
boolean selected,
|
||||
@Nullable View.OnClickListener listener
|
||||
) {
|
||||
return buildConversationRow(context, row, selectionMode, selected, false, listener);
|
||||
}
|
||||
|
||||
public static LinearLayout buildConversationRow(
|
||||
Context context,
|
||||
WechatSurfaceMapper.ConversationRow row,
|
||||
boolean selectionMode,
|
||||
boolean selected,
|
||||
boolean highlighted,
|
||||
@Nullable View.OnClickListener listener
|
||||
) {
|
||||
LinearLayout card = new LinearLayout(context);
|
||||
card.setOrientation(LinearLayout.HORIZONTAL);
|
||||
@@ -692,7 +703,7 @@ public final class BossUi {
|
||||
params.bottomMargin = dp(context, 1);
|
||||
card.setLayoutParams(params);
|
||||
card.setPadding(dp(context, 16), dp(context, 12), dp(context, 16), dp(context, 12));
|
||||
card.setBackgroundColor(row.pinnedConversation ? PINNED_ROW_BG : Color.WHITE);
|
||||
card.setBackgroundColor(highlighted ? Color.parseColor("#EAF8F0") : row.pinnedConversation ? PINNED_ROW_BG : Color.WHITE);
|
||||
card.setElevation(0f);
|
||||
if (listener != null) {
|
||||
card.setClickable(true);
|
||||
@@ -789,6 +800,23 @@ public final class BossUi {
|
||||
trailingColumn.addView(selector);
|
||||
}
|
||||
|
||||
if (highlighted) {
|
||||
TextView targetView = new TextView(context);
|
||||
targetView.setText("目标线程");
|
||||
targetView.setTextSize(11);
|
||||
targetView.setTypeface(Typeface.DEFAULT_BOLD);
|
||||
targetView.setTextColor(context.getColor(R.color.boss_green));
|
||||
targetView.setBackground(createRoundedBackground(Color.parseColor("#E7F7EE"), dp(context, 9)));
|
||||
targetView.setPadding(dp(context, 7), dp(context, 3), dp(context, 7), dp(context, 3));
|
||||
LinearLayout.LayoutParams targetParams = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
targetParams.topMargin = dp(context, 8);
|
||||
targetView.setLayoutParams(targetParams);
|
||||
trailingColumn.addView(targetView);
|
||||
}
|
||||
|
||||
if (row.unreadCount > 0) {
|
||||
TextView unreadView = new TextView(context);
|
||||
unreadView.setText(row.unreadCount > 99 ? "99+" : String.valueOf(row.unreadCount));
|
||||
|
||||
@@ -8,19 +8,41 @@ import androidx.annotation.Nullable;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ConversationFolderActivity extends BossScreenActivity {
|
||||
public static final String EXTRA_FOLDER_KEY = "folder_key";
|
||||
public static final String EXTRA_FOLDER_NAME = "folder_name";
|
||||
public static final String EXTRA_TARGET_PROJECT_ID = "target_project_id";
|
||||
public static final String EXTRA_TARGET_PROJECT_IDS = "target_project_ids";
|
||||
public static final String EXTRA_TARGET_PROJECT_LABEL = "target_project_label";
|
||||
|
||||
private String folderKey;
|
||||
private String folderName;
|
||||
private String targetProjectId;
|
||||
private ArrayList<String> targetProjectIds;
|
||||
private String targetProjectLabel;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
folderKey = getIntent().getStringExtra(EXTRA_FOLDER_KEY);
|
||||
folderName = getIntent().getStringExtra(EXTRA_FOLDER_NAME);
|
||||
configureScreen(folderName == null || folderName.isEmpty() ? "项目线程" : folderName, "同一项目下的线程列表");
|
||||
targetProjectId = getIntent().getStringExtra(EXTRA_TARGET_PROJECT_ID);
|
||||
targetProjectIds = new ArrayList<>();
|
||||
String[] extraTargetProjectIds = getIntent().getStringArrayExtra(EXTRA_TARGET_PROJECT_IDS);
|
||||
if (extraTargetProjectIds != null) {
|
||||
for (String item : extraTargetProjectIds) {
|
||||
if (item != null) {
|
||||
String trimmed = item.trim();
|
||||
if (!trimmed.isEmpty()) {
|
||||
targetProjectIds.add(trimmed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
targetProjectLabel = getIntent().getStringExtra(EXTRA_TARGET_PROJECT_LABEL);
|
||||
configureScreen(folderName == null || folderName.isEmpty() ? "项目线程" : folderName, "0 个线程");
|
||||
refreshButton.setVisibility(android.view.View.GONE);
|
||||
setHeaderAction("...", v -> showMoreMenu());
|
||||
reload();
|
||||
@@ -59,13 +81,12 @@ public class ConversationFolderActivity extends BossScreenActivity {
|
||||
}
|
||||
|
||||
String resolvedFolderName = folder.optString("folderLabel", folderName == null ? "项目线程" : folderName);
|
||||
String deviceName = folder.optString("deviceName", "");
|
||||
int threadCount = folder.optInt("threadCount", 0);
|
||||
configureScreen(resolvedFolderName, deviceName.isEmpty() ? "项目线程" : deviceName);
|
||||
configureScreen(resolvedFolderName, threadCount + " 个线程");
|
||||
appendContent(BossUi.buildSoftPanel(
|
||||
this,
|
||||
"项目内部线程页",
|
||||
resolvedFolderName,
|
||||
(deviceName.isEmpty() ? "当前设备" : deviceName) + "\n共 " + threadCount + " 个线程",
|
||||
"点击线程后进入具体聊天窗口。"
|
||||
));
|
||||
|
||||
@@ -75,26 +96,97 @@ public class ConversationFolderActivity extends BossScreenActivity {
|
||||
setRefreshing(false);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < threads.length(); i++) {
|
||||
JSONObject item = threads.optJSONObject(i);
|
||||
if (item == null) continue;
|
||||
String projectId = item.optString("projectId", "");
|
||||
WechatSurfaceMapper.ConversationRow row = WechatSurfaceMapper.toConversationRow(item);
|
||||
appendContent(BossUi.buildConversationRow(
|
||||
|
||||
ArrayList<Integer> targetIndices = resolveTargetThreadIndices(threads);
|
||||
if (!targetIndices.isEmpty()) {
|
||||
String matchedLabel = targetProjectLabel;
|
||||
if ((matchedLabel == null || matchedLabel.isEmpty())) {
|
||||
JSONObject firstTarget = threads.optJSONObject(targetIndices.get(0));
|
||||
if (firstTarget != null) {
|
||||
matchedLabel = firstTarget.optString("threadTitle", "");
|
||||
}
|
||||
}
|
||||
appendContent(BossUi.buildSoftPanel(
|
||||
this,
|
||||
row,
|
||||
v -> {
|
||||
if (projectId.isEmpty()) {
|
||||
showMessage("缺少 projectId");
|
||||
return;
|
||||
}
|
||||
openProject(projectId, row.threadTitle);
|
||||
}
|
||||
"已定位到目标线程",
|
||||
matchedLabel == null || matchedLabel.isEmpty()
|
||||
? "文件夹页已打开,并将匹配线程置顶显示。"
|
||||
: matchedLabel,
|
||||
targetIndices.size() + " 个匹配项已置顶"
|
||||
));
|
||||
}
|
||||
|
||||
for (int i = 0; i < targetIndices.size(); i++) {
|
||||
renderThreadAtIndex(threads, targetIndices.get(i), true);
|
||||
}
|
||||
for (int i = 0; i < threads.length(); i++) {
|
||||
if (!targetIndices.contains(i)) {
|
||||
renderThreadAtIndex(threads, i, false);
|
||||
}
|
||||
}
|
||||
setRefreshing(false);
|
||||
}
|
||||
|
||||
private void renderThreadAtIndex(JSONArray threads, int index, boolean highlighted) {
|
||||
JSONObject item = threads.optJSONObject(index);
|
||||
if (item == null) return;
|
||||
String projectId = item.optString("projectId", "");
|
||||
WechatSurfaceMapper.ConversationRow row = WechatSurfaceMapper.toConversationRow(item);
|
||||
appendContent(BossUi.buildConversationRow(
|
||||
this,
|
||||
row,
|
||||
false,
|
||||
false,
|
||||
highlighted,
|
||||
v -> {
|
||||
if (projectId.isEmpty()) {
|
||||
showMessage("缺少 projectId");
|
||||
return;
|
||||
}
|
||||
openProject(projectId, row.threadTitle);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
private ArrayList<Integer> resolveTargetThreadIndices(JSONArray threads) {
|
||||
ArrayList<Integer> targetIndices = new ArrayList<>();
|
||||
if (threads == null || threads.length() == 0) {
|
||||
return targetIndices;
|
||||
}
|
||||
|
||||
if (!targetProjectIds.isEmpty()) {
|
||||
for (int i = 0; i < threads.length(); i++) {
|
||||
JSONObject item = threads.optJSONObject(i);
|
||||
if (item == null) continue;
|
||||
String projectId = item.optString("projectId", "");
|
||||
if (targetProjectIds.contains(projectId)) {
|
||||
targetIndices.add(i);
|
||||
}
|
||||
}
|
||||
return targetIndices;
|
||||
}
|
||||
|
||||
if (targetProjectId != null && !targetProjectId.isEmpty()) {
|
||||
for (int i = 0; i < threads.length(); i++) {
|
||||
JSONObject item = threads.optJSONObject(i);
|
||||
if (item != null && targetProjectId.equals(item.optString("projectId", ""))) {
|
||||
targetIndices.add(i);
|
||||
return targetIndices;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetProjectLabel != null && !targetProjectLabel.isEmpty()) {
|
||||
for (int i = 0; i < threads.length(); i++) {
|
||||
JSONObject item = threads.optJSONObject(i);
|
||||
if (item != null && targetProjectLabel.equals(item.optString("threadTitle", ""))) {
|
||||
targetIndices.add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return targetIndices;
|
||||
}
|
||||
|
||||
private void openProject(String projectId, String projectName) {
|
||||
Intent intent = new Intent(this, ProjectDetailActivity.class);
|
||||
intent.putExtra(ProjectDetailActivity.EXTRA_PROJECT_ID, projectId);
|
||||
|
||||
@@ -1091,7 +1091,13 @@ public class MainActivity extends AppCompatActivity {
|
||||
showMessage("缺少 folderKey");
|
||||
return;
|
||||
}
|
||||
openConversationFolder(folderKey, resolveConversationFolderName(item, row));
|
||||
openConversationFolder(
|
||||
folderKey,
|
||||
resolveConversationFolderName(item, row),
|
||||
item.optString("searchMatchProjectId", ""),
|
||||
item.optJSONArray("searchMatchProjectIds"),
|
||||
item.optString("searchMatchLabel", "")
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (projectId.isEmpty()) {
|
||||
@@ -1319,6 +1325,11 @@ public class MainActivity extends AppCompatActivity {
|
||||
String matchLabel = resolveConversationSearchMatchLabel(item, query);
|
||||
if (!matchLabel.isEmpty()) {
|
||||
filteredItem.put("searchMatchLabel", matchLabel);
|
||||
JSONArray matchProjectIds = resolveConversationSearchMatchTargetIds(item, matchLabel);
|
||||
if (matchProjectIds.length() > 0) {
|
||||
filteredItem.put("searchMatchProjectId", matchProjectIds.optString(0, ""));
|
||||
filteredItem.put("searchMatchProjectIds", matchProjectIds);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// Keep the original item if JSON cloning fails.
|
||||
@@ -1466,6 +1477,33 @@ public class MainActivity extends AppCompatActivity {
|
||||
return "";
|
||||
}
|
||||
|
||||
private static JSONArray resolveConversationSearchMatchTargetIds(JSONObject item, String matchLabel) {
|
||||
JSONArray matchedProjectIds = new JSONArray();
|
||||
if (item == null || matchLabel == null || matchLabel.isEmpty()) {
|
||||
return matchedProjectIds;
|
||||
}
|
||||
JSONArray searchAliases = item.optJSONArray("searchAliases");
|
||||
JSONArray searchTargetProjectIds = item.optJSONArray("searchTargetProjectIds");
|
||||
if (searchAliases == null || searchTargetProjectIds == null) {
|
||||
JSONArray legacyTargets = item.optJSONArray("searchAliasTargets");
|
||||
if (searchAliases == null || legacyTargets == null) {
|
||||
return matchedProjectIds;
|
||||
}
|
||||
searchTargetProjectIds = legacyTargets;
|
||||
}
|
||||
int limit = Math.min(searchAliases.length(), searchTargetProjectIds.length());
|
||||
for (int i = 0; i < limit; i++) {
|
||||
String alias = searchAliases.optString(i, "").trim();
|
||||
if (!alias.isEmpty() && alias.equals(matchLabel)) {
|
||||
String projectId = searchTargetProjectIds.optString(i, "").trim();
|
||||
if (!projectId.isEmpty()) {
|
||||
matchedProjectIds.put(projectId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchedProjectIds;
|
||||
}
|
||||
|
||||
private void renderDevicesRoot() {
|
||||
if (screenList == null) {
|
||||
return;
|
||||
@@ -1583,9 +1621,37 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void openConversationFolder(String folderKey, String folderName) {
|
||||
openConversationFolder(folderKey, folderName, "", null, "");
|
||||
}
|
||||
|
||||
private void openConversationFolder(
|
||||
String folderKey,
|
||||
String folderName,
|
||||
String targetProjectId,
|
||||
JSONArray targetProjectIds,
|
||||
String targetProjectLabel
|
||||
) {
|
||||
Intent intent = new Intent(this, ConversationFolderActivity.class);
|
||||
intent.putExtra(ConversationFolderActivity.EXTRA_FOLDER_KEY, folderKey);
|
||||
intent.putExtra(ConversationFolderActivity.EXTRA_FOLDER_NAME, folderName);
|
||||
if (targetProjectId != null && !targetProjectId.isEmpty()) {
|
||||
intent.putExtra(ConversationFolderActivity.EXTRA_TARGET_PROJECT_ID, targetProjectId);
|
||||
}
|
||||
if (targetProjectIds != null && targetProjectIds.length() > 0) {
|
||||
ArrayList<String> ids = new ArrayList<>();
|
||||
for (int i = 0; i < targetProjectIds.length(); i++) {
|
||||
String id = targetProjectIds.optString(i, "").trim();
|
||||
if (!id.isEmpty()) {
|
||||
ids.add(id);
|
||||
}
|
||||
}
|
||||
if (!ids.isEmpty()) {
|
||||
intent.putExtra(ConversationFolderActivity.EXTRA_TARGET_PROJECT_IDS, ids.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
if (targetProjectLabel != null && !targetProjectLabel.isEmpty()) {
|
||||
intent.putExtra(ConversationFolderActivity.EXTRA_TARGET_PROJECT_LABEL, targetProjectLabel);
|
||||
}
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,11 +45,15 @@ public class ConversationFolderActivityTest {
|
||||
ImageButton headerAction = activity.findViewById(R.id.screen_header_action);
|
||||
ImageButton refreshButton = activity.findViewById(R.id.screen_refresh_button);
|
||||
LinearLayout content = activity.findViewById(R.id.screen_content);
|
||||
TextView titleView = activity.findViewById(R.id.screen_title);
|
||||
TextView subtitleView = activity.findViewById(R.id.screen_subtitle);
|
||||
|
||||
assertEquals("更多", String.valueOf(headerAction.getContentDescription()));
|
||||
assertEquals(View.GONE, refreshButton.getVisibility());
|
||||
assertEquals("Talking", String.valueOf(titleView.getText()));
|
||||
assertEquals("3 个线程", String.valueOf(subtitleView.getText()));
|
||||
assertTrue(viewTreeContainsText(content, "Talking"));
|
||||
assertTrue(viewTreeContainsText(content, "查询腾讯HAI GPU服务器"));
|
||||
assertTrue(viewTreeContainsText(content, "项目内部线程页"));
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(activity, "showMoreMenu");
|
||||
|
||||
@@ -59,27 +63,79 @@ public class ConversationFolderActivityTest {
|
||||
assertTrue(viewTreeContainsText(listView.getAdapter().getView(0, null, listView), "刷新"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void conversationFolderHighlightsTargetThreadWhenSearchTargetIsProvided() throws Exception {
|
||||
Intent intent = new Intent()
|
||||
.putExtra(ConversationFolderActivity.EXTRA_FOLDER_KEY, "talking")
|
||||
.putExtra(ConversationFolderActivity.EXTRA_FOLDER_NAME, "Talking")
|
||||
.putExtra(ConversationFolderActivity.EXTRA_TARGET_PROJECT_ID, "project-1")
|
||||
.putExtra(ConversationFolderActivity.EXTRA_TARGET_PROJECT_IDS, new String[]{"project-1", "project-2"})
|
||||
.putExtra(ConversationFolderActivity.EXTRA_TARGET_PROJECT_LABEL, "发布回滚");
|
||||
TestConversationFolderActivity activity = Robolectric
|
||||
.buildActivity(TestConversationFolderActivity.class, intent)
|
||||
.setup()
|
||||
.get();
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"renderFolder",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, buildFolderPayload())
|
||||
);
|
||||
|
||||
LinearLayout content = activity.findViewById(R.id.screen_content);
|
||||
assertTrue(viewTreeContainsText(content, "已定位到目标线程"));
|
||||
assertTrue(viewTreeContainsText(content, "目标线程"));
|
||||
assertTrue(viewTreeContainsText(content, "发布回滚"));
|
||||
assertEquals(2, countTextOccurrences(content, "目标线程"));
|
||||
assertTrue(countTextOccurrences(content, "发布回滚") >= 3);
|
||||
assertEquals(0, countTextOccurrences(content, "project-1"));
|
||||
}
|
||||
|
||||
private static JSONObject buildFolderPayload() throws Exception {
|
||||
JSONArray threads = new JSONArray()
|
||||
.put(new JSONObject()
|
||||
.put("projectId", "project-1")
|
||||
.put("threadTitle", "查询腾讯HAI GPU服务器")
|
||||
.put("threadTitle", "发布回滚")
|
||||
.put("folderLabel", "Talking")
|
||||
.put("lastMessagePreview", "已从设备导入线程")
|
||||
.put("timeLabel", "02:28"))
|
||||
.put(new JSONObject()
|
||||
.put("projectId", "project-2")
|
||||
.put("threadTitle", "状态栏显示CPU和内存")
|
||||
.put("threadTitle", "发布回滚")
|
||||
.put("folderLabel", "Talking")
|
||||
.put("lastMessagePreview", "已从设备导入线程")
|
||||
.put("timeLabel", "02:12"));
|
||||
.put("timeLabel", "02:12"))
|
||||
.put(new JSONObject()
|
||||
.put("projectId", "project-3")
|
||||
.put("threadTitle", "日志收口")
|
||||
.put("folderLabel", "Talking")
|
||||
.put("lastMessagePreview", "已从设备导入线程")
|
||||
.put("timeLabel", "02:05"));
|
||||
return new JSONObject()
|
||||
.put("folderLabel", "Talking")
|
||||
.put("deviceName", "Mac Studio")
|
||||
.put("threadCount", 2)
|
||||
.put("threadCount", 3)
|
||||
.put("threads", threads);
|
||||
}
|
||||
|
||||
private static int countTextOccurrences(View root, String expectedText) {
|
||||
int count = 0;
|
||||
if (root instanceof TextView) {
|
||||
CharSequence text = ((TextView) root).getText();
|
||||
if (expectedText.contentEquals(text)) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
if (!(root instanceof ViewGroup)) {
|
||||
return count;
|
||||
}
|
||||
ViewGroup group = (ViewGroup) root;
|
||||
for (int index = 0; index < group.getChildCount(); index += 1) {
|
||||
count += countTextOccurrences(group.getChildAt(index), expectedText);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static boolean viewTreeContainsText(View root, String expectedText) {
|
||||
if (root instanceof TextView) {
|
||||
CharSequence text = ((TextView) root).getText();
|
||||
|
||||
@@ -59,7 +59,8 @@ public class MainActivityConversationSearchTest {
|
||||
.put("projectTitle", "Boss")
|
||||
.put("threadTitle", "Boss")
|
||||
.put("folderLabel", "2 个线程 · 最近:发布回滚")
|
||||
.put("searchAliases", new JSONArray().put("发布回滚").put("Android UI 收尾"))
|
||||
.put("searchAliases", new JSONArray().put("发布回滚").put("发布回滚").put("Android UI 收尾"))
|
||||
.put("searchTargetProjectIds", new JSONArray().put("thread-revert-1").put("thread-revert-2").put("thread-ui"))
|
||||
.put("lastMessagePreview", "最近:发布回滚")
|
||||
.put("latestReplyLabel", "11:00"));
|
||||
|
||||
@@ -68,6 +69,9 @@ public class MainActivityConversationSearchTest {
|
||||
assertEquals(1, filtered.length());
|
||||
assertEquals("folder-boss", filtered.optJSONObject(0).optString("projectId", ""));
|
||||
assertEquals("发布回滚", filtered.optJSONObject(0).optString("searchMatchLabel", ""));
|
||||
assertEquals("thread-revert-1", filtered.optJSONObject(0).optString("searchMatchProjectId", ""));
|
||||
assertEquals(2, filtered.optJSONObject(0).optJSONArray("searchMatchProjectIds").length());
|
||||
assertEquals("thread-revert-2", filtered.optJSONObject(0).optJSONArray("searchMatchProjectIds").optString(1, ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -132,7 +136,8 @@ public class MainActivityConversationSearchTest {
|
||||
.put("threadTitle", "Boss")
|
||||
.put("lastMessagePreview", "最近:发布回滚")
|
||||
.put("latestReplyLabel", "11:00")
|
||||
.put("searchAliases", new JSONArray().put("发布回滚").put("Android UI 收尾"))));
|
||||
.put("searchAliases", new JSONArray().put("发布回滚").put("发布回滚").put("Android UI 收尾"))
|
||||
.put("searchTargetProjectIds", new JSONArray().put("thread-revert-1").put("thread-revert-2").put("thread-ui"))));
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(activity, "showContent");
|
||||
Shadows.shadowOf(activity.getMainLooper()).idle();
|
||||
@@ -154,6 +159,10 @@ public class MainActivityConversationSearchTest {
|
||||
assertEquals(ConversationFolderActivity.class.getName(), nextIntent.getComponent().getClassName());
|
||||
assertEquals("mac-studio:boss", nextIntent.getStringExtra(ConversationFolderActivity.EXTRA_FOLDER_KEY));
|
||||
assertEquals("Boss", nextIntent.getStringExtra(ConversationFolderActivity.EXTRA_FOLDER_NAME));
|
||||
assertEquals("thread-revert-1", nextIntent.getStringExtra(ConversationFolderActivity.EXTRA_TARGET_PROJECT_ID));
|
||||
assertEquals(2, nextIntent.getStringArrayExtra(ConversationFolderActivity.EXTRA_TARGET_PROJECT_IDS).length);
|
||||
assertEquals("thread-revert-2", nextIntent.getStringArrayExtra(ConversationFolderActivity.EXTRA_TARGET_PROJECT_IDS)[1]);
|
||||
assertEquals("发布回滚", nextIntent.getStringExtra(ConversationFolderActivity.EXTRA_TARGET_PROJECT_LABEL));
|
||||
}
|
||||
|
||||
private static JSONArray buildConversations() throws Exception {
|
||||
|
||||
@@ -239,6 +239,27 @@ public class MainActivityRealtimeTest {
|
||||
assertEquals(0, apiClient.conversationsCalls);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshConversationsData_fallsBackToFlatConversationsFeedWhenHomeFeedFails() throws Exception {
|
||||
MainActivity activity = Robolectric.buildActivity(MainActivity.class).setup().resume().get();
|
||||
Shadows.shadowOf(activity.getMainLooper()).idle();
|
||||
|
||||
RecordingRejectedConversationSourceClient apiClient = new RecordingRejectedConversationSourceClient(
|
||||
activity.getSharedPreferences("test-boss-api", Context.MODE_PRIVATE)
|
||||
);
|
||||
ReflectionHelpers.setField(activity, "apiClient", apiClient);
|
||||
ReflectionHelpers.callInstanceMethod(activity, "showContent");
|
||||
Shadows.shadowOf(activity.getMainLooper()).idle();
|
||||
|
||||
activity.refreshConversationsData();
|
||||
waitFor(() -> apiClient.homeCalls > 0 && apiClient.conversationsCalls > 0);
|
||||
|
||||
assertEquals(1, apiClient.homeCalls);
|
||||
assertEquals(1, apiClient.conversationsCalls);
|
||||
JSONArray conversationsData = ReflectionHelpers.getField(activity, "conversationsData");
|
||||
assertEquals("flat-thread", conversationsData.optJSONObject(0).optString("projectId", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshAllData_prefersConversationHomeFeedOverFlatConversationsFeed() throws Exception {
|
||||
MainActivity activity = Robolectric.buildActivity(MainActivity.class).setup().resume().get();
|
||||
@@ -262,6 +283,31 @@ public class MainActivityRealtimeTest {
|
||||
assertEquals(0, apiClient.conversationsCalls);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshAllData_fallsBackToFlatConversationsFeedWhenHomeFeedFails() throws Exception {
|
||||
MainActivity activity = Robolectric.buildActivity(MainActivity.class).setup().resume().get();
|
||||
Shadows.shadowOf(activity.getMainLooper()).idle();
|
||||
|
||||
RecordingRejectedConversationSourceClient apiClient = new RecordingRejectedConversationSourceClient(
|
||||
activity.getSharedPreferences("test-boss-api", Context.MODE_PRIVATE)
|
||||
);
|
||||
ReflectionHelpers.setField(activity, "apiClient", apiClient);
|
||||
ReflectionHelpers.callInstanceMethod(activity, "showContent");
|
||||
Shadows.shadowOf(activity.getMainLooper()).idle();
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(
|
||||
activity,
|
||||
"refreshAllData",
|
||||
ReflectionHelpers.ClassParameter.from(JSONObject.class, new JSONObject())
|
||||
);
|
||||
waitFor(() -> apiClient.homeCalls > 0 && apiClient.conversationsCalls > 0);
|
||||
|
||||
assertEquals(1, apiClient.homeCalls);
|
||||
assertEquals(1, apiClient.conversationsCalls);
|
||||
JSONArray conversationsData = ReflectionHelpers.getField(activity, "conversationsData");
|
||||
assertEquals("flat-thread", conversationsData.optJSONObject(0).optString("projectId", ""));
|
||||
}
|
||||
|
||||
private static void waitFor(BooleanSupplier condition) throws Exception {
|
||||
long deadlineAt = System.currentTimeMillis() + 2_000L;
|
||||
while (System.currentTimeMillis() < deadlineAt) {
|
||||
@@ -298,6 +344,81 @@ public class MainActivityRealtimeTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RecordingRejectedConversationSourceClient extends BossApiClient {
|
||||
int homeCalls;
|
||||
int conversationsCalls;
|
||||
int sessionCalls;
|
||||
int devicesCalls;
|
||||
int settingsCalls;
|
||||
int otaCalls;
|
||||
|
||||
RecordingRejectedConversationSourceClient(android.content.SharedPreferences prefs) {
|
||||
super(prefs, "https://boss.hyzq.net");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse getConversationHome() throws java.io.IOException, org.json.JSONException {
|
||||
homeCalls += 1;
|
||||
return new ApiResponse(200, new JSONObject()
|
||||
.put("ok", false)
|
||||
.put("message", "HOME_UNAVAILABLE"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse getConversations() throws java.io.IOException, org.json.JSONException {
|
||||
conversationsCalls += 1;
|
||||
return new ApiResponse(200, new JSONObject()
|
||||
.put("ok", true)
|
||||
.put("conversations", buildFlatConversations()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse getSession() throws java.io.IOException, org.json.JSONException {
|
||||
sessionCalls += 1;
|
||||
return new ApiResponse(200, new JSONObject()
|
||||
.put("ok", true)
|
||||
.put("session", new JSONObject()
|
||||
.put("account", "17600003315")
|
||||
.put("displayName", "Boss 超级管理员")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse getDevices() throws java.io.IOException, org.json.JSONException {
|
||||
devicesCalls += 1;
|
||||
return new ApiResponse(200, new JSONObject()
|
||||
.put("ok", true)
|
||||
.put("devices", new JSONArray()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse getSettings() throws java.io.IOException, org.json.JSONException {
|
||||
settingsCalls += 1;
|
||||
return new ApiResponse(200, new JSONObject()
|
||||
.put("ok", true)
|
||||
.put("settings", new JSONObject().put("preferredEntryPoint", "conversations"))
|
||||
.put("user", new JSONObject()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiResponse getOtaStatus() throws java.io.IOException, org.json.JSONException {
|
||||
otaCalls += 1;
|
||||
return new ApiResponse(200, new JSONObject()
|
||||
.put("ok", true)
|
||||
.put("hasOta", false));
|
||||
}
|
||||
|
||||
private static JSONArray buildFlatConversations() throws org.json.JSONException {
|
||||
return new JSONArray().put(new JSONObject()
|
||||
.put("projectId", "flat-thread")
|
||||
.put("conversationType", "single_device")
|
||||
.put("projectTitle", "发布回滚")
|
||||
.put("threadTitle", "发布回滚")
|
||||
.put("folderLabel", "Boss")
|
||||
.put("lastMessagePreview", "最近:发布回滚")
|
||||
.put("latestReplyLabel", "11:00"));
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RecordingConversationSourceClient extends BossApiClient {
|
||||
int homeCalls;
|
||||
int conversationsCalls;
|
||||
|
||||
Reference in New Issue
Block a user