feat: polish pinned conversations and context ring
This commit is contained in:
@@ -5,7 +5,10 @@ import android.animation.ObjectAnimator;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.text.TextUtils;
|
||||
@@ -38,6 +41,9 @@ public final class BossUi {
|
||||
private static final int DEVICE_STATUS_ONLINE = Color.parseColor("#18B85A");
|
||||
private static final int DEVICE_STATUS_ABNORMAL = Color.parseColor("#FF5A5A");
|
||||
private static final int DEVICE_STATUS_OFFLINE = Color.parseColor("#A7AFB7");
|
||||
private static final int PINNED_ROW_BG = Color.parseColor("#FFF7F7F7");
|
||||
private static final int CONTEXT_RING_TRACK = Color.parseColor("#FFD7D7D7");
|
||||
private static final int CONTEXT_RING_BG = Color.parseColor("#FFF4F4F4");
|
||||
|
||||
private BossUi() {}
|
||||
|
||||
@@ -570,7 +576,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(Color.WHITE);
|
||||
card.setBackgroundColor(row.pinnedConversation ? PINNED_ROW_BG : Color.WHITE);
|
||||
card.setElevation(0f);
|
||||
if (listener != null) {
|
||||
card.setClickable(true);
|
||||
@@ -630,7 +636,7 @@ public final class BossUi {
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
|
||||
if (!TextUtils.isEmpty(row.topPinnedLabel)) {
|
||||
if (!row.pinnedConversation && !TextUtils.isEmpty(row.topPinnedLabel)) {
|
||||
TextView pinnedView = new TextView(context);
|
||||
pinnedView.setText(row.topPinnedLabel);
|
||||
pinnedView.setTextSize(11);
|
||||
@@ -649,7 +655,7 @@ public final class BossUi {
|
||||
timeView.setText(TextUtils.isEmpty(row.timeLabel) ? "--:--" : row.timeLabel);
|
||||
timeView.setTextSize(12);
|
||||
timeView.setTextColor(context.getColor(R.color.boss_text_muted));
|
||||
timeView.setPadding(0, dp(context, TextUtils.isEmpty(row.topPinnedLabel) ? 2 : 8), 0, 0);
|
||||
timeView.setPadding(0, dp(context, TextUtils.isEmpty(row.topPinnedLabel) || row.pinnedConversation ? 2 : 8), 0, 0);
|
||||
timeView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
@@ -698,23 +704,73 @@ public final class BossUi {
|
||||
trailingColumn.addView(activityWrap);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(row.contextStatusLabel)) {
|
||||
TextView contextView = new TextView(context);
|
||||
contextView.setText(row.contextStatusLabel);
|
||||
contextView.setTextSize(11);
|
||||
contextView.setTypeface(Typeface.DEFAULT_BOLD);
|
||||
contextView.setTextColor(resolveConversationContextColor(context, row.contextStatusLevel));
|
||||
contextView.setPadding(0, dp(context, 8), 0, 0);
|
||||
contextView.setMaxLines(1);
|
||||
contextView.setEllipsize(TextUtils.TruncateAt.END);
|
||||
contextView.setGravity(Gravity.END);
|
||||
trailingColumn.addView(contextView);
|
||||
if (row.contextIndicatorVisible) {
|
||||
FrameLayout ringWrap = new FrameLayout(context);
|
||||
LinearLayout.LayoutParams ringWrapParams = new LinearLayout.LayoutParams(dp(context, 28), dp(context, 28));
|
||||
ringWrapParams.topMargin = dp(context, 8);
|
||||
ringWrap.setLayoutParams(ringWrapParams);
|
||||
ringWrap.setBackground(createRoundedBackground(CONTEXT_RING_BG, dp(context, 14)));
|
||||
ringWrap.setContentDescription(TextUtils.isEmpty(row.contextStatusLabel)
|
||||
? "上下文使用量"
|
||||
: "上下文使用量:" + row.contextStatusLabel);
|
||||
|
||||
View ring = buildContextUsageRing(
|
||||
context,
|
||||
row.contextUsagePercent,
|
||||
row.contextStatusLevel,
|
||||
row.contextMustFinish
|
||||
);
|
||||
FrameLayout.LayoutParams ringParams = new FrameLayout.LayoutParams(dp(context, 18), dp(context, 18), Gravity.CENTER);
|
||||
ring.setLayoutParams(ringParams);
|
||||
ringWrap.addView(ring);
|
||||
trailingColumn.addView(ringWrap);
|
||||
}
|
||||
|
||||
card.addView(trailingColumn);
|
||||
return card;
|
||||
}
|
||||
|
||||
public static LinearLayout buildConversationSectionHeader(
|
||||
Context context,
|
||||
String title,
|
||||
String actionLabel,
|
||||
@Nullable View.OnClickListener listener
|
||||
) {
|
||||
LinearLayout row = new LinearLayout(context);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
row.setGravity(Gravity.CENTER_VERTICAL);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
params.leftMargin = dp(context, 16);
|
||||
params.rightMargin = dp(context, 16);
|
||||
params.bottomMargin = dp(context, 8);
|
||||
row.setLayoutParams(params);
|
||||
row.setPadding(0, dp(context, 6), 0, dp(context, 4));
|
||||
if (listener != null) {
|
||||
row.setClickable(true);
|
||||
row.setFocusable(true);
|
||||
row.setOnClickListener(listener);
|
||||
}
|
||||
|
||||
TextView titleView = new TextView(context);
|
||||
titleView.setText(title);
|
||||
titleView.setTextSize(13);
|
||||
titleView.setTypeface(Typeface.DEFAULT_BOLD);
|
||||
titleView.setTextColor(context.getColor(R.color.boss_text_muted));
|
||||
LinearLayout.LayoutParams titleParams = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f);
|
||||
titleView.setLayoutParams(titleParams);
|
||||
row.addView(titleView);
|
||||
|
||||
TextView actionView = new TextView(context);
|
||||
actionView.setText(actionLabel);
|
||||
actionView.setTextSize(12);
|
||||
actionView.setTextColor(context.getColor(R.color.boss_text_soft));
|
||||
row.addView(actionView);
|
||||
return row;
|
||||
}
|
||||
|
||||
public static LinearLayout buildEmptyCard(Context context, String text) {
|
||||
return buildCard(context, "暂无内容", text, "下拉或点击顶部刷新按钮重试。");
|
||||
}
|
||||
@@ -1166,6 +1222,10 @@ public final class BossUi {
|
||||
return Math.round(value * context.getResources().getDisplayMetrics().density);
|
||||
}
|
||||
|
||||
public static int dp(Context context, float value) {
|
||||
return Math.round(value * context.getResources().getDisplayMetrics().density);
|
||||
}
|
||||
|
||||
private static View buildConversationAvatar(Context context, WechatSurfaceMapper.ConversationRow row) {
|
||||
if (!row.isGroup) {
|
||||
return buildAvatarCircle(
|
||||
@@ -1362,6 +1422,62 @@ public final class BossUi {
|
||||
}
|
||||
}
|
||||
|
||||
private static View buildContextUsageRing(
|
||||
Context context,
|
||||
int usagePercent,
|
||||
@Nullable String level,
|
||||
boolean mustFinish
|
||||
) {
|
||||
final int clampedUsage = Math.max(0, Math.min(100, usagePercent));
|
||||
final int ringColor = resolveContextRingColor(level, mustFinish);
|
||||
final float strokeWidth = dp(context, 2.4f);
|
||||
return new View(context) {
|
||||
private final Paint trackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Paint progressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final RectF arcRect = new RectF();
|
||||
|
||||
{
|
||||
trackPaint.setStyle(Paint.Style.STROKE);
|
||||
trackPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||
trackPaint.setStrokeWidth(strokeWidth);
|
||||
trackPaint.setColor(CONTEXT_RING_TRACK);
|
||||
|
||||
progressPaint.setStyle(Paint.Style.STROKE);
|
||||
progressPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||
progressPaint.setStrokeWidth(strokeWidth);
|
||||
progressPaint.setColor(ringColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
float inset = strokeWidth;
|
||||
arcRect.set(inset, inset, getWidth() - inset, getHeight() - inset);
|
||||
canvas.drawArc(arcRect, -90, 360, false, trackPaint);
|
||||
canvas.drawArc(arcRect, -90, (360f * clampedUsage) / 100f, false, progressPaint);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static int resolveContextRingColor(@Nullable String level, boolean mustFinish) {
|
||||
if (mustFinish) {
|
||||
return Color.parseColor("#D94B4B");
|
||||
}
|
||||
if (TextUtils.isEmpty(level)) {
|
||||
return Color.parseColor("#8E8E93");
|
||||
}
|
||||
switch (level) {
|
||||
case "critical":
|
||||
return Color.parseColor("#D94B4B");
|
||||
case "urgent":
|
||||
return Color.parseColor("#8A8A8A");
|
||||
case "watch":
|
||||
return Color.parseColor("#9A9A9A");
|
||||
default:
|
||||
return Color.parseColor("#B0B0B0");
|
||||
}
|
||||
}
|
||||
|
||||
private static String firstLetter(String value) {
|
||||
String text = value == null ? "" : value.trim();
|
||||
if (text.isEmpty()) {
|
||||
|
||||
@@ -64,6 +64,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
private @Nullable String boundDeviceId;
|
||||
private @Nullable String boundDeviceName;
|
||||
private String conversationSearchQuery = "";
|
||||
private boolean pinnedConversationsCollapsed = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
@@ -464,9 +465,39 @@ public class MainActivity extends AppCompatActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
JSONArray pinnedItems = new JSONArray();
|
||||
JSONArray regularItems = new JSONArray();
|
||||
for (int i = 0; i < filteredConversations.length(); i++) {
|
||||
JSONObject item = filteredConversations.optJSONObject(i);
|
||||
if (item == null) continue;
|
||||
if (!item.optString("topPinnedLabel", "").isEmpty()) {
|
||||
pinnedItems.put(item);
|
||||
} else {
|
||||
regularItems.put(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (pinnedItems.length() > 0) {
|
||||
screenContent.addView(BossUi.buildConversationSectionHeader(
|
||||
this,
|
||||
"置顶会话",
|
||||
pinnedConversationsCollapsed ? "展开" : "收起",
|
||||
v -> {
|
||||
pinnedConversationsCollapsed = !pinnedConversationsCollapsed;
|
||||
renderConversationsRoot();
|
||||
}
|
||||
));
|
||||
if (!pinnedConversationsCollapsed) {
|
||||
appendConversationRows(pinnedItems);
|
||||
}
|
||||
}
|
||||
appendConversationRows(regularItems);
|
||||
}
|
||||
|
||||
private void appendConversationRows(JSONArray items) {
|
||||
for (int i = 0; i < items.length(); i++) {
|
||||
JSONObject item = items.optJSONObject(i);
|
||||
if (item == null) continue;
|
||||
String projectId = item.optString("projectId", "");
|
||||
String conversationType = item.optString("conversationType", "");
|
||||
String folderKey = item.optString("folderKey", "");
|
||||
@@ -475,21 +506,21 @@ public class MainActivity extends AppCompatActivity {
|
||||
this,
|
||||
row,
|
||||
v -> {
|
||||
if ("folder_archive".equals(conversationType)) {
|
||||
if (folderKey.isEmpty()) {
|
||||
showMessage("缺少 folderKey");
|
||||
return;
|
||||
}
|
||||
openConversationFolder(folderKey, row.threadTitle);
|
||||
return;
|
||||
}
|
||||
if (projectId.isEmpty()) {
|
||||
showMessage("缺少 projectId");
|
||||
return;
|
||||
}
|
||||
String projectName = row.threadTitle.isEmpty() ? "未命名会话" : row.threadTitle;
|
||||
openProject(projectId, projectName);
|
||||
}));
|
||||
if ("folder_archive".equals(conversationType)) {
|
||||
if (folderKey.isEmpty()) {
|
||||
showMessage("缺少 folderKey");
|
||||
return;
|
||||
}
|
||||
openConversationFolder(folderKey, row.threadTitle);
|
||||
return;
|
||||
}
|
||||
if (projectId.isEmpty()) {
|
||||
showMessage("缺少 projectId");
|
||||
return;
|
||||
}
|
||||
String projectName = row.threadTitle.isEmpty() ? "未命名会话" : row.threadTitle;
|
||||
openProject(projectId, projectName);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,20 +54,25 @@ public final class WechatSurfaceMapper {
|
||||
}
|
||||
JSONObject avatar = source.optJSONObject("avatar");
|
||||
boolean isGroup = source.optBoolean("isGroup", groupAvatarMembers.size() > 1);
|
||||
String pinnedLabel = source.optString("topPinnedLabel", "");
|
||||
return new ConversationRow(
|
||||
source.optString("threadTitle", source.optString("title", source.optString("projectTitle", ""))),
|
||||
source.optString("folderLabel", ""),
|
||||
source.optString("lastMessagePreview", source.optString("preview", "")),
|
||||
source.optString("timeLabel", source.optString("latestReplyLabel", "")),
|
||||
source.optInt("unreadCount", 0),
|
||||
source.optString("topPinnedLabel", ""),
|
||||
pinnedLabel,
|
||||
source.optInt("activityIconCount", 0),
|
||||
isGroup,
|
||||
isGroup ? "" : avatar == null ? "" : avatar.optString("primary", ""),
|
||||
isGroup ? "" : avatar == null ? "" : avatar.optString("secondary", ""),
|
||||
groupAvatarMembers.toArray(new GroupAvatarMember[0]),
|
||||
pinnedLabel != null && !pinnedLabel.isEmpty(),
|
||||
buildContextStatusLabel(source),
|
||||
resolveContextStatusLevel(source)
|
||||
resolveContextStatusLevel(source),
|
||||
resolveContextUsagePercent(source),
|
||||
hasContextIndicator(source),
|
||||
source.optBoolean("mustFinishBeforeCompaction", false)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -199,6 +204,25 @@ public final class WechatSurfaceMapper {
|
||||
return indicator.optString("level", null);
|
||||
}
|
||||
|
||||
private static int resolveContextUsagePercent(JSONObject source) {
|
||||
if (source.optBoolean("mustFinishBeforeCompaction", false)) {
|
||||
return 100;
|
||||
}
|
||||
JSONObject indicator = source.optJSONObject("contextBudgetIndicator");
|
||||
if (indicator == null || !indicator.optBoolean("visible", false)) {
|
||||
return -1;
|
||||
}
|
||||
int remainingPercent = indicator.optInt("percent", -1);
|
||||
if (remainingPercent < 0) {
|
||||
return -1;
|
||||
}
|
||||
return Math.max(0, Math.min(100, 100 - remainingPercent));
|
||||
}
|
||||
|
||||
private static boolean hasContextIndicator(JSONObject source) {
|
||||
return resolveContextUsagePercent(source) >= 0;
|
||||
}
|
||||
|
||||
public static RootTopAction rootTopAction(String activeTab, boolean refreshing) {
|
||||
if ("devices".equals(activeTab)) {
|
||||
return new RootTopAction("+添加", true, false, "add_device");
|
||||
@@ -380,8 +404,12 @@ public final class WechatSurfaceMapper {
|
||||
public final String avatarPrimary;
|
||||
public final String avatarSecondary;
|
||||
public final GroupAvatarMember[] groupAvatarMembers;
|
||||
public final boolean pinnedConversation;
|
||||
public final String contextStatusLabel;
|
||||
public final String contextStatusLevel;
|
||||
public final int contextUsagePercent;
|
||||
public final boolean contextIndicatorVisible;
|
||||
public final boolean contextMustFinish;
|
||||
|
||||
public ConversationRow(
|
||||
String threadTitle,
|
||||
@@ -408,8 +436,12 @@ public final class WechatSurfaceMapper {
|
||||
avatarPrimary,
|
||||
avatarSecondary,
|
||||
groupAvatarMembers,
|
||||
false,
|
||||
null,
|
||||
null
|
||||
null,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@@ -425,8 +457,12 @@ public final class WechatSurfaceMapper {
|
||||
String avatarPrimary,
|
||||
String avatarSecondary,
|
||||
GroupAvatarMember[] groupAvatarMembers,
|
||||
boolean pinnedConversation,
|
||||
String contextStatusLabel,
|
||||
String contextStatusLevel
|
||||
String contextStatusLevel,
|
||||
int contextUsagePercent,
|
||||
boolean contextIndicatorVisible,
|
||||
boolean contextMustFinish
|
||||
) {
|
||||
this.threadTitle = threadTitle;
|
||||
this.folderLabel = folderLabel;
|
||||
@@ -439,8 +475,12 @@ public final class WechatSurfaceMapper {
|
||||
this.avatarPrimary = avatarPrimary;
|
||||
this.avatarSecondary = avatarSecondary;
|
||||
this.groupAvatarMembers = groupAvatarMembers == null ? new GroupAvatarMember[0] : groupAvatarMembers;
|
||||
this.pinnedConversation = pinnedConversation;
|
||||
this.contextStatusLabel = contextStatusLabel;
|
||||
this.contextStatusLevel = contextStatusLevel;
|
||||
this.contextUsagePercent = contextUsagePercent;
|
||||
this.contextIndicatorVisible = contextIndicatorVisible;
|
||||
this.contextMustFinish = contextMustFinish;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
@@ -80,15 +82,49 @@ public class BossUiConversationRowTest {
|
||||
"M",
|
||||
"W",
|
||||
new WechatSurfaceMapper.GroupAvatarMember[0],
|
||||
false,
|
||||
"上下文紧张 34%",
|
||||
"urgent"
|
||||
"urgent",
|
||||
66,
|
||||
true,
|
||||
false
|
||||
);
|
||||
|
||||
LinearLayout rowView = BossUi.buildConversationRow(context, row, null);
|
||||
LinearLayout trailingColumn = (LinearLayout) rowView.getChildAt(2);
|
||||
|
||||
assertTrue(viewTreeContainsText(trailingColumn, "上下文紧张 34%"));
|
||||
assertEquals("空闲会话不应再渲染活动点", 2, trailingColumn.getChildCount());
|
||||
assertFalse("右下角应改成环形上下文状态,而不是文字", viewTreeContainsText(trailingColumn, "上下文紧张 34%"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildConversationRow_usesSubtlePinnedBackgroundWithoutPinnedBadge() {
|
||||
Context context = RuntimeEnvironment.getApplication();
|
||||
WechatSurfaceMapper.ConversationRow row = new WechatSurfaceMapper.ConversationRow(
|
||||
"主 Agent",
|
||||
"主控线程",
|
||||
"正在观察多个任务",
|
||||
"09:26",
|
||||
0,
|
||||
"置顶",
|
||||
0,
|
||||
false,
|
||||
"M",
|
||||
"A",
|
||||
new WechatSurfaceMapper.GroupAvatarMember[0],
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
-1,
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
LinearLayout rowView = BossUi.buildConversationRow(context, row, null);
|
||||
|
||||
assertTrue("置顶会话背景应略深于普通白底", rowView.getBackground() instanceof ColorDrawable);
|
||||
assertEquals(0xFFF7F7F7, ((ColorDrawable) rowView.getBackground()).getColor());
|
||||
assertFalse("置顶会话不应再显示右侧“置顶”文字", viewTreeContainsText(rowView, "置顶"));
|
||||
}
|
||||
|
||||
private static boolean viewTreeContainsText(View root, String expectedText) {
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.view.View;
|
||||
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 MainActivityPinnedConversationsTest {
|
||||
@Test
|
||||
public void renderConversationsRoot_groupsPinnedConversationsAndTogglesCollapse() throws Exception {
|
||||
MainActivity activity = Robolectric.buildActivity(MainActivity.class).setup().get();
|
||||
ReflectionHelpers.setField(activity, "conversationsData", new JSONArray()
|
||||
.put(new JSONObject()
|
||||
.put("projectId", "p1")
|
||||
.put("projectTitle", "主 Agent")
|
||||
.put("threadTitle", "主 Agent")
|
||||
.put("folderLabel", "主控线程")
|
||||
.put("topPinnedLabel", "置顶")
|
||||
.put("lastMessagePreview", "正在观察")
|
||||
.put("latestReplyLabel", "09:40"))
|
||||
.put(new JSONObject()
|
||||
.put("projectId", "p2")
|
||||
.put("projectTitle", "Boss 移动控制台")
|
||||
.put("threadTitle", "Boss 移动控制台")
|
||||
.put("folderLabel", "归档确认")
|
||||
.put("lastMessagePreview", "线程链路正常")
|
||||
.put("latestReplyLabel", "09:41")));
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(activity, "showContent");
|
||||
ReflectionHelpers.callInstanceMethod(activity, "renderConversationsRoot");
|
||||
|
||||
LinearLayout content = activity.findViewById(R.id.screen_content);
|
||||
assertTrue(viewTreeContainsText(content, "置顶会话"));
|
||||
assertTrue(viewTreeContainsText(content, "收起"));
|
||||
assertTrue(viewTreeContainsText(content, "主 Agent"));
|
||||
assertTrue(viewTreeContainsText(content, "Boss 移动控制台"));
|
||||
|
||||
View pinnedHeader = content.getChildAt(1);
|
||||
pinnedHeader.performClick();
|
||||
|
||||
assertEquals(true, ReflectionHelpers.getField(activity, "pinnedConversationsCollapsed"));
|
||||
assertTrue(viewTreeContainsText(content, "展开"));
|
||||
assertTrue("收起后普通会话仍应保留", viewTreeContainsText(content, "Boss 移动控制台"));
|
||||
}
|
||||
|
||||
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 LinearLayout)) {
|
||||
return false;
|
||||
}
|
||||
LinearLayout group = (LinearLayout) root;
|
||||
for (int index = 0; index < group.getChildCount(); index += 1) {
|
||||
if (viewTreeContainsText(group.getChildAt(index), expectedText)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,8 @@ public class WechatSurfaceMapperConversationStatusTest {
|
||||
assertEquals(0, row.activityIconCount);
|
||||
assertEquals("上下文紧张 34%", row.contextStatusLabel);
|
||||
assertEquals("urgent", row.contextStatusLevel);
|
||||
assertEquals(66, row.contextUsagePercent);
|
||||
assertEquals(true, row.contextIndicatorVisible);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -44,6 +46,8 @@ public class WechatSurfaceMapperConversationStatusTest {
|
||||
|
||||
assertEquals("必须收尾", row.contextStatusLabel);
|
||||
assertEquals("critical", row.contextStatusLevel);
|
||||
assertEquals(100, row.contextUsagePercent);
|
||||
assertEquals(true, row.contextMustFinish);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -60,6 +64,7 @@ public class WechatSurfaceMapperConversationStatusTest {
|
||||
|
||||
assertNull(row.contextStatusLabel);
|
||||
assertNull(row.contextStatusLevel);
|
||||
assertEquals(-1, row.contextUsagePercent);
|
||||
}
|
||||
|
||||
private static final class StubJSONObject extends JSONObject {
|
||||
|
||||
Reference in New Issue
Block a user