1811 lines
73 KiB
Java
1811 lines
73 KiB
Java
package com.hyzq.boss;
|
||
|
||
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.widget.ImageButton;
|
||
import android.text.Layout;
|
||
import android.text.TextUtils;
|
||
import android.util.TypedValue;
|
||
import android.view.Gravity;
|
||
import android.view.View;
|
||
import android.widget.Button;
|
||
import android.widget.EditText;
|
||
import android.widget.FrameLayout;
|
||
import android.widget.LinearLayout;
|
||
import android.widget.TextView;
|
||
import android.widget.Toast;
|
||
|
||
import androidx.annotation.Nullable;
|
||
import androidx.core.widget.ImageViewCompat;
|
||
import android.content.res.ColorStateList;
|
||
|
||
import org.json.JSONObject;
|
||
|
||
import java.util.ArrayList;
|
||
import java.util.List;
|
||
|
||
public final class BossUi {
|
||
private static final int[] AVATAR_BG_COLORS = {
|
||
Color.parseColor("#1EC76F"),
|
||
Color.parseColor("#DFF5EC"),
|
||
Color.parseColor("#DCEEF6"),
|
||
Color.parseColor("#FCE5BF")
|
||
};
|
||
private static final int[] ACTIVITY_ICON_COLORS = {
|
||
Color.parseColor("#C4CCC8"),
|
||
Color.parseColor("#B5C0BA"),
|
||
Color.parseColor("#A7B3AD"),
|
||
Color.parseColor("#98A6A0")
|
||
};
|
||
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("#FFD0D0D0");
|
||
private static final int CONTEXT_RING_BG = Color.parseColor("#FFFFFFFF");
|
||
|
||
private BossUi() {}
|
||
|
||
public enum TopActionButtonStyle {
|
||
PRIMARY_TEXT,
|
||
SECONDARY_TEXT,
|
||
COMPACT_ICON
|
||
}
|
||
|
||
public enum TopActionIcon {
|
||
BACK,
|
||
CLOSE,
|
||
MORE,
|
||
ADD,
|
||
SEARCH,
|
||
REFRESH,
|
||
EDIT,
|
||
SAVE,
|
||
INFO
|
||
}
|
||
|
||
public static String formatRoleLabel(@Nullable String rawRole) {
|
||
if (TextUtils.isEmpty(rawRole)) {
|
||
return "";
|
||
}
|
||
switch (rawRole) {
|
||
case "highest_admin":
|
||
return "最高管理员";
|
||
case "admin":
|
||
return "管理员";
|
||
case "member":
|
||
return "成员";
|
||
case "primary":
|
||
return "主 GPT";
|
||
case "backup":
|
||
return "备用 GPT";
|
||
case "api_fallback":
|
||
return "API 容灾";
|
||
default:
|
||
return rawRole;
|
||
}
|
||
}
|
||
|
||
public static void applyTopActionButtonStyle(Context context, Button button, TopActionButtonStyle style) {
|
||
if (style == TopActionButtonStyle.COMPACT_ICON) {
|
||
button.setBackgroundResource(R.drawable.bg_secondary_button);
|
||
button.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
|
||
button.setMinWidth(dp(context, 38));
|
||
button.setMinimumWidth(dp(context, 38));
|
||
button.setMinHeight(dp(context, 36));
|
||
button.setMinimumHeight(dp(context, 36));
|
||
button.setPadding(dp(context, 12), dp(context, 6), dp(context, 12), dp(context, 6));
|
||
return;
|
||
}
|
||
|
||
if (style == TopActionButtonStyle.PRIMARY_TEXT) {
|
||
button.setBackgroundResource(R.drawable.bg_primary_button);
|
||
button.setTextColor(context.getColor(R.color.boss_surface));
|
||
} else {
|
||
button.setBackgroundResource(R.drawable.bg_secondary_button);
|
||
button.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
}
|
||
button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
|
||
button.setMinWidth(0);
|
||
button.setMinimumWidth(0);
|
||
button.setMinHeight(0);
|
||
button.setMinimumHeight(0);
|
||
button.setPadding(dp(context, 12), dp(context, 8), dp(context, 12), dp(context, 8));
|
||
}
|
||
|
||
public static void applyTopIconButtonStyle(Context context, ImageButton button) {
|
||
button.setBackgroundResource(R.drawable.bg_top_icon_button);
|
||
button.setMinimumWidth(dp(context, 40));
|
||
button.setMinimumHeight(dp(context, 40));
|
||
button.setPadding(dp(context, 8), dp(context, 8), dp(context, 8), dp(context, 8));
|
||
ImageViewCompat.setImageTintList(button, ColorStateList.valueOf(context.getColor(R.color.boss_text_primary)));
|
||
}
|
||
|
||
public static void bindTopIconButton(Context context, ImageButton button, TopActionIcon icon, String contentDescription) {
|
||
applyTopIconButtonStyle(context, button);
|
||
button.setImageResource(resolveTopActionIconRes(icon));
|
||
button.setContentDescription(normalizeTopActionContentDescription(contentDescription, icon));
|
||
}
|
||
|
||
public static String normalizeTopActionContentDescription(@Nullable String raw, TopActionIcon icon) {
|
||
if (!TextUtils.isEmpty(raw)) {
|
||
if ("...".contentEquals(raw)) {
|
||
return "更多";
|
||
}
|
||
return raw;
|
||
}
|
||
switch (icon) {
|
||
case BACK:
|
||
return "返回";
|
||
case CLOSE:
|
||
return "关闭";
|
||
case ADD:
|
||
return "新增";
|
||
case SEARCH:
|
||
return "搜索";
|
||
case REFRESH:
|
||
return "刷新";
|
||
case EDIT:
|
||
return "编辑";
|
||
case SAVE:
|
||
return "保存";
|
||
case INFO:
|
||
return "信息";
|
||
case MORE:
|
||
default:
|
||
return "更多";
|
||
}
|
||
}
|
||
|
||
public static TopActionIcon topActionIconForLabel(@Nullable String label) {
|
||
if (TextUtils.isEmpty(label)) {
|
||
return TopActionIcon.MORE;
|
||
}
|
||
if ("...".contentEquals(label)) {
|
||
return TopActionIcon.MORE;
|
||
}
|
||
if ("保存".contentEquals(label)) {
|
||
return TopActionIcon.SAVE;
|
||
}
|
||
if ("刷新".contentEquals(label)) {
|
||
return TopActionIcon.REFRESH;
|
||
}
|
||
if ("新增".contentEquals(label)) {
|
||
return TopActionIcon.ADD;
|
||
}
|
||
if ("编辑".contentEquals(label) || "编辑目标".contentEquals(label)) {
|
||
return TopActionIcon.EDIT;
|
||
}
|
||
if ("只读".contentEquals(label)) {
|
||
return TopActionIcon.INFO;
|
||
}
|
||
return TopActionIcon.MORE;
|
||
}
|
||
|
||
private static int resolveTopActionIconRes(TopActionIcon icon) {
|
||
switch (icon) {
|
||
case BACK:
|
||
return R.drawable.ic_boss_back;
|
||
case CLOSE:
|
||
return R.drawable.ic_boss_close;
|
||
case ADD:
|
||
return R.drawable.ic_boss_add;
|
||
case SEARCH:
|
||
return R.drawable.ic_boss_search;
|
||
case REFRESH:
|
||
return R.drawable.ic_boss_refresh;
|
||
case EDIT:
|
||
return R.drawable.ic_boss_edit;
|
||
case SAVE:
|
||
return R.drawable.ic_boss_check;
|
||
case INFO:
|
||
return R.drawable.ic_boss_info;
|
||
case MORE:
|
||
default:
|
||
return R.drawable.ic_boss_more;
|
||
}
|
||
}
|
||
|
||
public static LinearLayout buildListRow(
|
||
Context context,
|
||
String title,
|
||
@Nullable String subtitle,
|
||
@Nullable String meta,
|
||
@Nullable String badge,
|
||
@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.bottomMargin = dp(context, 1);
|
||
row.setLayoutParams(params);
|
||
row.setPadding(dp(context, 16), dp(context, 14), dp(context, 16), dp(context, 14));
|
||
row.setBackgroundResource(R.drawable.bg_list_row);
|
||
if (listener != null) {
|
||
row.setClickable(true);
|
||
row.setFocusable(true);
|
||
row.setOnClickListener(listener);
|
||
}
|
||
|
||
LinearLayout textWrap = new LinearLayout(context);
|
||
textWrap.setOrientation(LinearLayout.VERTICAL);
|
||
LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(
|
||
0,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
1f
|
||
);
|
||
textWrap.setLayoutParams(textParams);
|
||
|
||
TextView titleView = new TextView(context);
|
||
titleView.setText(title);
|
||
titleView.setTextSize(17);
|
||
titleView.setTypeface(Typeface.DEFAULT_BOLD);
|
||
titleView.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
textWrap.addView(titleView);
|
||
|
||
if (!TextUtils.isEmpty(subtitle)) {
|
||
TextView subtitleView = new TextView(context);
|
||
subtitleView.setText(subtitle);
|
||
subtitleView.setTextSize(14);
|
||
subtitleView.setTextColor(context.getColor(R.color.boss_text_muted));
|
||
subtitleView.setPadding(0, dp(context, 4), 0, 0);
|
||
textWrap.addView(subtitleView);
|
||
}
|
||
|
||
if (!TextUtils.isEmpty(meta)) {
|
||
TextView metaView = new TextView(context);
|
||
metaView.setText(meta);
|
||
metaView.setTextSize(12);
|
||
metaView.setTextColor(context.getColor(R.color.boss_text_soft));
|
||
metaView.setPadding(0, dp(context, 6), 0, 0);
|
||
textWrap.addView(metaView);
|
||
}
|
||
|
||
row.addView(textWrap);
|
||
|
||
LinearLayout accessoryWrap = new LinearLayout(context);
|
||
accessoryWrap.setOrientation(LinearLayout.VERTICAL);
|
||
accessoryWrap.setGravity(Gravity.END | Gravity.CENTER_VERTICAL);
|
||
|
||
if (!TextUtils.isEmpty(badge)) {
|
||
TextView badgeView = new TextView(context);
|
||
badgeView.setText(badge);
|
||
badgeView.setTextSize(12);
|
||
badgeView.setTextColor(context.getColor(R.color.boss_green));
|
||
badgeView.setBackgroundResource(R.drawable.bg_tab_active);
|
||
badgeView.setPadding(dp(context, 8), dp(context, 2), dp(context, 8), dp(context, 2));
|
||
accessoryWrap.addView(badgeView);
|
||
}
|
||
|
||
if (listener != null) {
|
||
TextView arrowView = new TextView(context);
|
||
arrowView.setText("›");
|
||
arrowView.setTextSize(18);
|
||
arrowView.setTextColor(context.getColor(R.color.boss_text_soft));
|
||
arrowView.setPadding(dp(context, 10), dp(context, 4), 0, 0);
|
||
accessoryWrap.addView(arrowView);
|
||
}
|
||
|
||
row.addView(accessoryWrap);
|
||
return row;
|
||
}
|
||
|
||
public static LinearLayout buildCard(Context context, String title, String body, String meta) {
|
||
return buildCard(context, title, body, meta, null);
|
||
}
|
||
|
||
public static LinearLayout buildCard(
|
||
Context context,
|
||
String title,
|
||
String body,
|
||
String meta,
|
||
@Nullable View.OnClickListener listener
|
||
) {
|
||
LinearLayout card = new LinearLayout(context);
|
||
card.setOrientation(LinearLayout.VERTICAL);
|
||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
params.bottomMargin = dp(context, 12);
|
||
card.setLayoutParams(params);
|
||
card.setPadding(dp(context, 18), dp(context, 18), dp(context, 18), dp(context, 18));
|
||
card.setBackgroundResource(R.drawable.bg_card);
|
||
if (listener != null) {
|
||
card.setClickable(true);
|
||
card.setFocusable(true);
|
||
card.setOnClickListener(listener);
|
||
}
|
||
|
||
TextView titleView = new TextView(context);
|
||
titleView.setText(title);
|
||
titleView.setTextSize(18);
|
||
titleView.setTypeface(Typeface.DEFAULT_BOLD);
|
||
titleView.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
|
||
TextView bodyView = new TextView(context);
|
||
bodyView.setText(body);
|
||
bodyView.setTextSize(14);
|
||
bodyView.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
bodyView.setPadding(0, dp(context, 8), 0, 0);
|
||
|
||
TextView metaView = new TextView(context);
|
||
metaView.setText(meta);
|
||
metaView.setTextSize(12);
|
||
metaView.setTextColor(context.getColor(R.color.boss_text_muted));
|
||
metaView.setPadding(0, dp(context, 10), 0, 0);
|
||
|
||
card.addView(titleView);
|
||
card.addView(bodyView);
|
||
card.addView(metaView);
|
||
return card;
|
||
}
|
||
|
||
public static LinearLayout buildMenuRow(
|
||
Context context,
|
||
String title,
|
||
String description,
|
||
@Nullable String badge,
|
||
View.OnClickListener listener
|
||
) {
|
||
return buildListRow(context, title, description, null, badge, listener);
|
||
}
|
||
|
||
public static LinearLayout buildWechatMenuRow(
|
||
Context context,
|
||
String title,
|
||
@Nullable String subtitle,
|
||
@Nullable String meta,
|
||
@Nullable String badge,
|
||
@Nullable View.OnClickListener listener
|
||
) {
|
||
LinearLayout row = buildListRow(context, title, subtitle, meta, badge, listener);
|
||
row.setBackgroundColor(Color.WHITE);
|
||
row.setElevation(0f);
|
||
row.setPadding(dp(context, 18), dp(context, 15), dp(context, 18), dp(context, 15));
|
||
return row;
|
||
}
|
||
|
||
public static LinearLayout buildSimpleProfileHeader(
|
||
Context context,
|
||
String name,
|
||
String subtitle,
|
||
@Nullable String detail
|
||
) {
|
||
LinearLayout card = new LinearLayout(context);
|
||
card.setOrientation(LinearLayout.HORIZONTAL);
|
||
card.setGravity(Gravity.CENTER_VERTICAL);
|
||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
params.bottomMargin = dp(context, 10);
|
||
card.setLayoutParams(params);
|
||
card.setPadding(dp(context, 20), dp(context, 18), dp(context, 20), dp(context, 18));
|
||
card.setBackgroundColor(Color.WHITE);
|
||
card.setElevation(0f);
|
||
|
||
TextView avatar = new TextView(context);
|
||
LinearLayout.LayoutParams avatarParams = new LinearLayout.LayoutParams(dp(context, 70), dp(context, 70));
|
||
avatar.setLayoutParams(avatarParams);
|
||
avatar.setGravity(Gravity.CENTER);
|
||
avatar.setText(firstLetter(name));
|
||
avatar.setTextSize(30);
|
||
avatar.setTypeface(Typeface.DEFAULT_BOLD);
|
||
avatar.setTextColor(context.getColor(R.color.boss_green));
|
||
avatar.setBackground(createRoundedBackground(Color.parseColor("#DFF3E8"), dp(context, 35)));
|
||
card.addView(avatar);
|
||
|
||
LinearLayout textWrap = new LinearLayout(context);
|
||
textWrap.setOrientation(LinearLayout.VERTICAL);
|
||
LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(
|
||
0,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
1f
|
||
);
|
||
textParams.leftMargin = dp(context, 14);
|
||
textWrap.setLayoutParams(textParams);
|
||
|
||
TextView titleView = new TextView(context);
|
||
titleView.setText(TextUtils.isEmpty(name) ? "我的" : name);
|
||
titleView.setTextSize(22);
|
||
titleView.setTypeface(Typeface.DEFAULT_BOLD);
|
||
titleView.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
textWrap.addView(titleView);
|
||
|
||
TextView subtitleView = new TextView(context);
|
||
subtitleView.setText(subtitle);
|
||
subtitleView.setTextSize(14);
|
||
subtitleView.setTextColor(context.getColor(R.color.boss_text_muted));
|
||
subtitleView.setPadding(0, dp(context, 5), 0, 0);
|
||
textWrap.addView(subtitleView);
|
||
|
||
if (!TextUtils.isEmpty(detail)) {
|
||
TextView detailView = new TextView(context);
|
||
detailView.setText(detail);
|
||
detailView.setTextSize(12);
|
||
detailView.setTextColor(context.getColor(R.color.boss_text_soft));
|
||
detailView.setPadding(0, dp(context, 5), 0, 0);
|
||
textWrap.addView(detailView);
|
||
}
|
||
|
||
card.addView(textWrap);
|
||
return card;
|
||
}
|
||
|
||
public static LinearLayout buildFormCell(
|
||
Context context,
|
||
String label,
|
||
@Nullable String helper,
|
||
View field
|
||
) {
|
||
LinearLayout cell = new LinearLayout(context);
|
||
cell.setOrientation(LinearLayout.VERTICAL);
|
||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
params.leftMargin = dp(context, 12);
|
||
params.rightMargin = dp(context, 12);
|
||
params.bottomMargin = dp(context, 10);
|
||
cell.setLayoutParams(params);
|
||
cell.setPadding(dp(context, 16), dp(context, 14), dp(context, 16), dp(context, 14));
|
||
cell.setBackground(createRoundedBackground(Color.WHITE, dp(context, 18)));
|
||
|
||
TextView labelView = new TextView(context);
|
||
labelView.setText(label);
|
||
labelView.setTextSize(14);
|
||
labelView.setTypeface(Typeface.DEFAULT_BOLD);
|
||
labelView.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
cell.addView(labelView);
|
||
|
||
if (!TextUtils.isEmpty(helper)) {
|
||
TextView helperView = new TextView(context);
|
||
helperView.setText(helper);
|
||
helperView.setTextSize(12);
|
||
helperView.setTextColor(context.getColor(R.color.boss_text_soft));
|
||
helperView.setPadding(0, dp(context, 4), 0, dp(context, 10));
|
||
cell.addView(helperView);
|
||
}
|
||
|
||
field.setPadding(field.getPaddingLeft(), field.getPaddingTop(), field.getPaddingRight(), field.getPaddingBottom());
|
||
cell.addView(field);
|
||
return cell;
|
||
}
|
||
|
||
public static LinearLayout buildDeviceCard(
|
||
Context context,
|
||
WechatSurfaceMapper.DeviceRow row,
|
||
@Nullable View.OnClickListener listener,
|
||
@Nullable View.OnLongClickListener longClickListener
|
||
) {
|
||
LinearLayout card = new LinearLayout(context);
|
||
card.setOrientation(LinearLayout.HORIZONTAL);
|
||
card.setGravity(Gravity.CENTER_VERTICAL);
|
||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
params.leftMargin = dp(context, 12);
|
||
params.rightMargin = dp(context, 12);
|
||
params.bottomMargin = dp(context, 12);
|
||
card.setLayoutParams(params);
|
||
card.setPadding(dp(context, 14), dp(context, 14), dp(context, 14), dp(context, 14));
|
||
card.setBackground(createRoundedBackground(Color.WHITE, dp(context, 18)));
|
||
card.setElevation(dp(context, 1));
|
||
if (listener != null) {
|
||
card.setClickable(true);
|
||
card.setFocusable(true);
|
||
card.setOnClickListener(listener);
|
||
}
|
||
if (longClickListener != null) {
|
||
card.setOnLongClickListener(longClickListener);
|
||
}
|
||
|
||
FrameLayout avatarWrap = new FrameLayout(context);
|
||
LinearLayout.LayoutParams avatarWrapParams = new LinearLayout.LayoutParams(dp(context, 56), dp(context, 56));
|
||
avatarWrap.setLayoutParams(avatarWrapParams);
|
||
|
||
TextView avatar = new TextView(context);
|
||
FrameLayout.LayoutParams avatarParams = new FrameLayout.LayoutParams(dp(context, 52), dp(context, 52));
|
||
avatar.setLayoutParams(avatarParams);
|
||
avatar.setGravity(Gravity.CENTER);
|
||
avatar.setText(firstLetter(firstNonEmpty(row.avatarLabel, row.title, "设")));
|
||
avatar.setTextSize(22);
|
||
avatar.setTypeface(Typeface.DEFAULT_BOLD);
|
||
avatar.setTextColor(resolveDeviceAccentColor(row.statusKey));
|
||
avatar.setBackground(createRoundedBackground(resolveDeviceAvatarBackground(row.statusKey), dp(context, 18)));
|
||
avatarWrap.addView(avatar);
|
||
|
||
View statusDot = new View(context);
|
||
FrameLayout.LayoutParams dotParams = new FrameLayout.LayoutParams(dp(context, 10), dp(context, 10), Gravity.END | Gravity.CENTER_VERTICAL);
|
||
dotParams.rightMargin = dp(context, 1);
|
||
statusDot.setLayoutParams(dotParams);
|
||
statusDot.setBackground(createRoundedBackground(resolveDeviceAccentColor(row.statusKey), dp(context, 5)));
|
||
avatarWrap.addView(statusDot);
|
||
|
||
card.addView(avatarWrap);
|
||
|
||
LinearLayout textWrap = new LinearLayout(context);
|
||
textWrap.setOrientation(LinearLayout.VERTICAL);
|
||
LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(
|
||
0,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
1f
|
||
);
|
||
textParams.leftMargin = dp(context, 12);
|
||
textWrap.setLayoutParams(textParams);
|
||
|
||
TextView titleView = new TextView(context);
|
||
titleView.setText(TextUtils.isEmpty(row.title) ? "设备" : row.title);
|
||
titleView.setTextSize(17);
|
||
titleView.setTypeface(Typeface.DEFAULT_BOLD);
|
||
titleView.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
titleView.setMaxLines(1);
|
||
titleView.setEllipsize(TextUtils.TruncateAt.END);
|
||
textWrap.addView(titleView);
|
||
|
||
TextView subtitleView = new TextView(context);
|
||
subtitleView.setText(row.subtitle);
|
||
subtitleView.setTextSize(13);
|
||
subtitleView.setTextColor(context.getColor(R.color.boss_text_muted));
|
||
subtitleView.setPadding(0, dp(context, 5), 0, 0);
|
||
subtitleView.setMaxLines(2);
|
||
subtitleView.setEllipsize(TextUtils.TruncateAt.END);
|
||
textWrap.addView(subtitleView);
|
||
|
||
TextView metaView = new TextView(context);
|
||
metaView.setText(row.meta);
|
||
metaView.setTextSize(12);
|
||
metaView.setTextColor(resolveDeviceMetaColor(context, row.statusKey));
|
||
metaView.setPadding(0, dp(context, 6), 0, 0);
|
||
metaView.setMaxLines(2);
|
||
metaView.setEllipsize(TextUtils.TruncateAt.END);
|
||
textWrap.addView(metaView);
|
||
|
||
card.addView(textWrap);
|
||
|
||
if (listener != null) {
|
||
TextView arrowView = new TextView(context);
|
||
arrowView.setText("›");
|
||
arrowView.setTextSize(18);
|
||
arrowView.setTextColor(context.getColor(R.color.boss_text_soft));
|
||
arrowView.setPadding(dp(context, 8), 0, 0, 0);
|
||
card.addView(arrowView);
|
||
}
|
||
return card;
|
||
}
|
||
|
||
public static LinearLayout buildSoftPanel(
|
||
Context context,
|
||
String title,
|
||
String body,
|
||
@Nullable String meta
|
||
) {
|
||
LinearLayout card = new LinearLayout(context);
|
||
card.setOrientation(LinearLayout.VERTICAL);
|
||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
params.leftMargin = dp(context, 12);
|
||
params.rightMargin = dp(context, 12);
|
||
params.bottomMargin = dp(context, 12);
|
||
card.setLayoutParams(params);
|
||
card.setPadding(dp(context, 16), dp(context, 16), dp(context, 16), dp(context, 16));
|
||
card.setBackground(createRoundedBackground(Color.parseColor("#E7F5ED"), dp(context, 18)));
|
||
|
||
TextView titleView = new TextView(context);
|
||
titleView.setText(title);
|
||
titleView.setTextSize(16);
|
||
titleView.setTypeface(Typeface.DEFAULT_BOLD);
|
||
titleView.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
card.addView(titleView);
|
||
|
||
TextView bodyView = new TextView(context);
|
||
bodyView.setText(body);
|
||
bodyView.setTextSize(14);
|
||
bodyView.setLineSpacing(0f, 1.2f);
|
||
bodyView.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
bodyView.setPadding(0, dp(context, 8), 0, 0);
|
||
card.addView(bodyView);
|
||
|
||
if (!TextUtils.isEmpty(meta)) {
|
||
TextView metaView = new TextView(context);
|
||
metaView.setText(meta);
|
||
metaView.setTextSize(12);
|
||
metaView.setTextColor(context.getColor(R.color.boss_text_muted));
|
||
metaView.setPadding(0, dp(context, 10), 0, 0);
|
||
card.addView(metaView);
|
||
}
|
||
return card;
|
||
}
|
||
|
||
public static LinearLayout buildInlineActionRow(Context context, Button... buttons) {
|
||
LinearLayout row = new LinearLayout(context);
|
||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||
LinearLayout.LayoutParams rowParams = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
rowParams.leftMargin = dp(context, 12);
|
||
rowParams.rightMargin = dp(context, 12);
|
||
rowParams.bottomMargin = dp(context, 12);
|
||
row.setLayoutParams(rowParams);
|
||
|
||
for (int i = 0; i < buttons.length; i++) {
|
||
Button button = buttons[i];
|
||
LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
|
||
0,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
1f
|
||
);
|
||
if (i > 0) {
|
||
buttonParams.leftMargin = dp(context, 8);
|
||
}
|
||
button.setLayoutParams(buttonParams);
|
||
row.addView(button);
|
||
}
|
||
return row;
|
||
}
|
||
|
||
public static Button buildMiniActionButton(Context context, String label, boolean primary) {
|
||
Button button = new Button(context);
|
||
button.setText(label);
|
||
button.setAllCaps(false);
|
||
button.setTextSize(13);
|
||
button.setPadding(dp(context, 10), dp(context, 10), dp(context, 10), dp(context, 10));
|
||
button.setTextColor(context.getColor(primary ? R.color.boss_surface : R.color.boss_green));
|
||
button.setBackgroundResource(primary ? R.drawable.bg_primary_button : R.drawable.bg_secondary_button);
|
||
return button;
|
||
}
|
||
|
||
public static LinearLayout buildConversationRow(
|
||
Context context,
|
||
WechatSurfaceMapper.ConversationRow row,
|
||
@Nullable View.OnClickListener listener
|
||
) {
|
||
return buildConversationRow(context, row, false, false, listener);
|
||
}
|
||
|
||
public static LinearLayout buildConversationRow(
|
||
Context context,
|
||
WechatSurfaceMapper.ConversationRow row,
|
||
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);
|
||
card.setGravity(Gravity.TOP);
|
||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
params.bottomMargin = dp(context, 1);
|
||
card.setLayoutParams(params);
|
||
card.setPadding(dp(context, 16), dp(context, 12), dp(context, 16), dp(context, 12));
|
||
card.setBackgroundColor(highlighted ? Color.parseColor("#EAF8F0") : row.pinnedConversation ? PINNED_ROW_BG : Color.WHITE);
|
||
card.setElevation(0f);
|
||
if (listener != null) {
|
||
card.setClickable(true);
|
||
card.setFocusable(true);
|
||
card.setOnClickListener(listener);
|
||
}
|
||
if (selectionMode) {
|
||
card.setContentDescription(selected ? "已选中会话" : "未选中会话");
|
||
}
|
||
|
||
card.addView(buildConversationAvatar(context, row));
|
||
|
||
LinearLayout centerColumn = new LinearLayout(context);
|
||
centerColumn.setOrientation(LinearLayout.VERTICAL);
|
||
LinearLayout.LayoutParams centerParams = new LinearLayout.LayoutParams(
|
||
0,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
1f
|
||
);
|
||
centerParams.leftMargin = dp(context, 12);
|
||
centerParams.rightMargin = dp(context, 10);
|
||
centerColumn.setLayoutParams(centerParams);
|
||
|
||
TextView titleView = new TextView(context);
|
||
titleView.setText(TextUtils.isEmpty(row.threadTitle) ? "未命名会话" : row.threadTitle);
|
||
titleView.setTextSize(17);
|
||
titleView.setTypeface(Typeface.DEFAULT_BOLD);
|
||
titleView.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
titleView.setMaxLines(1);
|
||
titleView.setEllipsize(TextUtils.TruncateAt.END);
|
||
centerColumn.addView(titleView);
|
||
|
||
if (!TextUtils.isEmpty(row.folderLabel)) {
|
||
TextView folderView = new TextView(context);
|
||
folderView.setText(row.folderLabel);
|
||
folderView.setTextSize(13);
|
||
folderView.setTextColor(context.getColor(R.color.boss_text_muted));
|
||
folderView.setPadding(0, dp(context, 4), 0, 0);
|
||
folderView.setMaxLines(1);
|
||
folderView.setEllipsize(TextUtils.TruncateAt.END);
|
||
centerColumn.addView(folderView);
|
||
}
|
||
|
||
TextView previewView = new TextView(context);
|
||
previewView.setText(TextUtils.isEmpty(row.lastMessagePreview) ? "暂无消息" : row.lastMessagePreview);
|
||
previewView.setTextSize(13);
|
||
previewView.setTextColor(context.getColor(R.color.boss_text_soft));
|
||
previewView.setPadding(0, dp(context, 4), 0, 0);
|
||
previewView.setMaxLines(1);
|
||
previewView.setEllipsize(TextUtils.TruncateAt.END);
|
||
centerColumn.addView(previewView);
|
||
|
||
card.addView(centerColumn);
|
||
|
||
LinearLayout trailingColumn = new LinearLayout(context);
|
||
trailingColumn.setOrientation(LinearLayout.VERTICAL);
|
||
trailingColumn.setGravity(Gravity.END);
|
||
trailingColumn.setLayoutParams(new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
));
|
||
|
||
if (!row.pinnedConversation && !TextUtils.isEmpty(row.topPinnedLabel)) {
|
||
TextView pinnedView = new TextView(context);
|
||
pinnedView.setText(row.topPinnedLabel);
|
||
pinnedView.setTextSize(11);
|
||
pinnedView.setTypeface(Typeface.DEFAULT_BOLD);
|
||
pinnedView.setTextColor(context.getColor(R.color.boss_green));
|
||
pinnedView.setBackground(createRoundedBackground(Color.parseColor("#E7F7EE"), dp(context, 9)));
|
||
pinnedView.setPadding(dp(context, 7), dp(context, 3), dp(context, 7), dp(context, 3));
|
||
pinnedView.setLayoutParams(new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
));
|
||
trailingColumn.addView(pinnedView);
|
||
}
|
||
|
||
TextView timeView = new TextView(context);
|
||
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) || row.pinnedConversation ? 2 : 8), 0, 0);
|
||
timeView.setLayoutParams(new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
));
|
||
trailingColumn.addView(timeView);
|
||
|
||
if (selectionMode) {
|
||
View selector = buildConversationSelectionIndicator(context, selected);
|
||
LinearLayout.LayoutParams selectorParams = new LinearLayout.LayoutParams(dp(context, 20), dp(context, 20));
|
||
selectorParams.topMargin = dp(context, 8);
|
||
selector.setLayoutParams(selectorParams);
|
||
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));
|
||
unreadView.setTextSize(11);
|
||
unreadView.setTypeface(Typeface.DEFAULT_BOLD);
|
||
unreadView.setTextColor(Color.WHITE);
|
||
unreadView.setGravity(Gravity.CENTER);
|
||
unreadView.setMinWidth(dp(context, 20));
|
||
unreadView.setBackground(createRoundedBackground(Color.parseColor("#FF5A5A"), dp(context, 10)));
|
||
unreadView.setPadding(dp(context, 6), dp(context, 2), dp(context, 6), dp(context, 2));
|
||
LinearLayout.LayoutParams unreadParams = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
unreadParams.topMargin = dp(context, 8);
|
||
unreadView.setLayoutParams(unreadParams);
|
||
trailingColumn.addView(unreadView);
|
||
}
|
||
|
||
int activityCount = Math.max(0, Math.min(row.activityIconCount, WechatSurfaceMapper.maxConversationActivityIcons()));
|
||
if (activityCount > 0) {
|
||
LinearLayout activityWrap = new LinearLayout(context);
|
||
activityWrap.setOrientation(LinearLayout.HORIZONTAL);
|
||
activityWrap.setGravity(Gravity.END | Gravity.CENTER_VERTICAL);
|
||
LinearLayout.LayoutParams activityParams = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
activityParams.topMargin = dp(context, 8);
|
||
activityWrap.setLayoutParams(activityParams);
|
||
for (int i = 0; i < activityCount; i++) {
|
||
View dot = buildAnimatedActivityDot(context, i);
|
||
if (i > 0) {
|
||
LinearLayout.LayoutParams dotParams = (LinearLayout.LayoutParams) dot.getLayoutParams();
|
||
dotParams.leftMargin = dp(context, 4);
|
||
dot.setLayoutParams(dotParams);
|
||
}
|
||
activityWrap.addView(dot);
|
||
}
|
||
trailingColumn.addView(activityWrap);
|
||
}
|
||
|
||
if (row.contextIndicatorVisible) {
|
||
FrameLayout ringWrap = new FrameLayout(context);
|
||
LinearLayout.LayoutParams ringWrapParams = new LinearLayout.LayoutParams(dp(context, 24), dp(context, 24));
|
||
ringWrapParams.topMargin = dp(context, 6);
|
||
ringWrap.setLayoutParams(ringWrapParams);
|
||
ringWrap.setBackground(createRoundedBackground(CONTEXT_RING_BG, dp(context, 12)));
|
||
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, 16), dp(context, 16), Gravity.CENTER);
|
||
ring.setLayoutParams(ringParams);
|
||
ringWrap.addView(ring);
|
||
trailingColumn.addView(ringWrap);
|
||
}
|
||
|
||
card.addView(trailingColumn);
|
||
return card;
|
||
}
|
||
|
||
private static View buildConversationSelectionIndicator(Context context, boolean selected) {
|
||
View indicator = new View(context);
|
||
GradientDrawable drawable = new GradientDrawable();
|
||
drawable.setShape(GradientDrawable.OVAL);
|
||
drawable.setColor(selected ? context.getColor(R.color.boss_green) : Color.WHITE);
|
||
drawable.setStroke(dp(context, 1.5f), selected ? context.getColor(R.color.boss_green) : Color.parseColor("#FFC8C8C8"));
|
||
indicator.setBackground(drawable);
|
||
indicator.setContentDescription(selected ? "已选中会话" : "未选中会话");
|
||
return indicator;
|
||
}
|
||
|
||
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, 6);
|
||
row.setLayoutParams(params);
|
||
row.setPadding(0, dp(context, 4), 0, dp(context, 2));
|
||
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, "下拉或点击顶部刷新按钮重试。");
|
||
}
|
||
|
||
public static TextView buildHintPill(Context context, String text) {
|
||
TextView pill = new TextView(context);
|
||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
params.leftMargin = dp(context, 12);
|
||
params.rightMargin = dp(context, 12);
|
||
params.bottomMargin = dp(context, 12);
|
||
pill.setLayoutParams(params);
|
||
pill.setText(text);
|
||
pill.setTextSize(12);
|
||
pill.setTextColor(context.getColor(R.color.boss_text_muted));
|
||
pill.setPadding(dp(context, 12), dp(context, 7), dp(context, 12), dp(context, 7));
|
||
pill.setBackground(createRoundedBackground(Color.parseColor("#F4F5F2"), dp(context, 14)));
|
||
return pill;
|
||
}
|
||
|
||
public static LinearLayout buildMessageBubble(
|
||
Context context,
|
||
String senderLabel,
|
||
String body,
|
||
@Nullable String meta,
|
||
boolean outgoing,
|
||
@Nullable String kindLabel
|
||
) {
|
||
LinearLayout wrapper = buildMessageWrapper(context, senderLabel, meta, outgoing);
|
||
|
||
LinearLayout bubble = new LinearLayout(context);
|
||
bubble.setOrientation(LinearLayout.VERTICAL);
|
||
bubble.setBackgroundResource(outgoing ? R.drawable.bg_message_outgoing : R.drawable.bg_message_incoming);
|
||
bubble.setPadding(dp(context, 14), dp(context, 12), dp(context, 14), dp(context, 12));
|
||
int maxBubbleWidth = Math.round(context.getResources().getDisplayMetrics().widthPixels * 0.72f);
|
||
bubble.setMinimumWidth(dp(context, 84));
|
||
|
||
if (!TextUtils.isEmpty(kindLabel)) {
|
||
TextView kindView = new TextView(context);
|
||
kindView.setText(kindLabel);
|
||
kindView.setTextSize(11);
|
||
kindView.setTypeface(Typeface.DEFAULT_BOLD);
|
||
kindView.setTextColor(context.getColor(outgoing ? R.color.boss_surface : R.color.boss_text_muted));
|
||
kindView.setPadding(0, 0, 0, dp(context, 6));
|
||
bubble.addView(kindView);
|
||
}
|
||
|
||
TextView bodyView = new TextView(context);
|
||
bodyView.setText(BossMarkdown.render(context, body, outgoing));
|
||
bodyView.setTextSize(15);
|
||
bodyView.setLineSpacing(0f, 1.34f);
|
||
bodyView.setTextColor(context.getColor(outgoing ? R.color.boss_surface : R.color.boss_text_primary));
|
||
bodyView.setMaxWidth(maxBubbleWidth);
|
||
bodyView.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY);
|
||
bodyView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
|
||
bubble.addView(bodyView);
|
||
|
||
wrapper.addView(bubble);
|
||
return wrapper;
|
||
}
|
||
|
||
public static LinearLayout buildAttachmentMessageCard(
|
||
Context context,
|
||
String senderLabel,
|
||
String sourceType,
|
||
String fileName,
|
||
@Nullable String detail,
|
||
@Nullable String status,
|
||
@Nullable String summary,
|
||
@Nullable String actionLabel,
|
||
@Nullable View.OnClickListener actionListener,
|
||
@Nullable String meta,
|
||
boolean outgoing,
|
||
@Nullable View.OnClickListener cardClickListener
|
||
) {
|
||
LinearLayout wrapper = buildMessageWrapper(context, senderLabel, meta, outgoing);
|
||
|
||
LinearLayout card = new LinearLayout(context);
|
||
card.setOrientation(LinearLayout.VERTICAL);
|
||
card.setMinimumWidth(dp(context, 180));
|
||
card.setPadding(dp(context, 14), dp(context, 12), dp(context, 14), dp(context, 12));
|
||
card.setBackground(createRoundedBackground(
|
||
outgoing ? Color.parseColor("#CFF0D8") : Color.WHITE,
|
||
dp(context, 18)
|
||
));
|
||
card.setElevation(dp(context, 1));
|
||
if (cardClickListener != null) {
|
||
card.setClickable(true);
|
||
card.setFocusable(true);
|
||
card.setOnClickListener(cardClickListener);
|
||
}
|
||
|
||
if ("image".equals(sourceType) || "video".equals(sourceType)) {
|
||
TextView preview = new TextView(context);
|
||
LinearLayout.LayoutParams previewParams = new LinearLayout.LayoutParams(
|
||
Math.round(context.getResources().getDisplayMetrics().widthPixels * 0.56f),
|
||
dp(context, 118)
|
||
);
|
||
preview.setLayoutParams(previewParams);
|
||
preview.setGravity(Gravity.CENTER);
|
||
preview.setText("image".equals(sourceType) ? "图片预览" : "视频封面");
|
||
preview.setTextSize(13);
|
||
preview.setTypeface(Typeface.DEFAULT_BOLD);
|
||
preview.setTextColor(context.getColor(R.color.boss_text_muted));
|
||
preview.setBackground(createRoundedBackground(Color.parseColor("#EEF2EE"), dp(context, 14)));
|
||
card.addView(preview);
|
||
|
||
TextView nameView = buildAttachmentPrimaryText(context, fileName);
|
||
nameView.setPadding(0, dp(context, 10), 0, 0);
|
||
card.addView(nameView);
|
||
|
||
TextView statusView = buildAttachmentSecondaryText(
|
||
context,
|
||
TextUtils.isEmpty(status) ? "已发送" : status
|
||
);
|
||
statusView.setPadding(0, dp(context, 6), 0, 0);
|
||
card.addView(statusView);
|
||
} else {
|
||
LinearLayout row = new LinearLayout(context);
|
||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||
row.setGravity(Gravity.CENTER_VERTICAL);
|
||
|
||
TextView icon = new TextView(context);
|
||
LinearLayout.LayoutParams iconParams = new LinearLayout.LayoutParams(dp(context, 42), dp(context, 42));
|
||
icon.setLayoutParams(iconParams);
|
||
icon.setGravity(Gravity.CENTER);
|
||
icon.setText("文");
|
||
icon.setTextSize(15);
|
||
icon.setTypeface(Typeface.DEFAULT_BOLD);
|
||
icon.setTextColor(context.getColor(R.color.boss_green));
|
||
icon.setBackground(createRoundedBackground(Color.parseColor("#E8F6ED"), dp(context, 12)));
|
||
row.addView(icon);
|
||
|
||
LinearLayout texts = new LinearLayout(context);
|
||
texts.setOrientation(LinearLayout.VERTICAL);
|
||
LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(
|
||
0,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
1f
|
||
);
|
||
textParams.leftMargin = dp(context, 12);
|
||
texts.setLayoutParams(textParams);
|
||
|
||
texts.addView(buildAttachmentPrimaryText(context, fileName));
|
||
texts.addView(buildAttachmentSecondaryText(
|
||
context,
|
||
joinAttachmentDetail(detail, status)
|
||
));
|
||
row.addView(texts);
|
||
|
||
card.addView(row);
|
||
}
|
||
|
||
if (!TextUtils.isEmpty(summary)) {
|
||
TextView summaryView = buildAttachmentSecondaryText(context, summary);
|
||
summaryView.setPadding(0, dp(context, 10), 0, 0);
|
||
summaryView.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
card.addView(summaryView);
|
||
}
|
||
|
||
if (!TextUtils.isEmpty(actionLabel) && actionListener != null) {
|
||
Button actionButton = new Button(context);
|
||
LinearLayout.LayoutParams actionParams = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
dp(context, 34)
|
||
);
|
||
actionParams.topMargin = dp(context, 12);
|
||
actionButton.setLayoutParams(actionParams);
|
||
actionButton.setMinWidth(dp(context, 88));
|
||
actionButton.setText(actionLabel);
|
||
actionButton.setAllCaps(false);
|
||
actionButton.setTextSize(13);
|
||
actionButton.setPadding(dp(context, 14), 0, dp(context, 14), 0);
|
||
actionButton.setBackgroundResource(R.drawable.bg_secondary_button);
|
||
actionButton.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
actionButton.setOnClickListener(actionListener);
|
||
card.addView(actionButton);
|
||
}
|
||
|
||
wrapper.addView(card);
|
||
return wrapper;
|
||
}
|
||
|
||
public static LinearLayout buildForwardSingleBubble(
|
||
Context context,
|
||
String senderLabel,
|
||
String body,
|
||
@Nullable String meta,
|
||
@Nullable String sourceLabel,
|
||
boolean outgoing
|
||
) {
|
||
LinearLayout wrapper = buildMessageBubble(
|
||
context,
|
||
senderLabel,
|
||
body,
|
||
meta,
|
||
outgoing,
|
||
ProjectChatUiState.labelForForwardKind("forward_single")
|
||
);
|
||
if (wrapper.getChildCount() < 2 || !(wrapper.getChildAt(1) instanceof LinearLayout)) {
|
||
return wrapper;
|
||
}
|
||
LinearLayout bubble = (LinearLayout) wrapper.getChildAt(1);
|
||
if (!TextUtils.isEmpty(sourceLabel)) {
|
||
TextView sourceView = new TextView(context);
|
||
sourceView.setText("来自 " + sourceLabel);
|
||
sourceView.setTextSize(12);
|
||
sourceView.setTextColor(context.getColor(outgoing ? R.color.boss_surface : R.color.boss_text_muted));
|
||
sourceView.setAlpha(outgoing ? 0.85f : 1f);
|
||
sourceView.setPadding(0, 0, 0, dp(context, 8));
|
||
bubble.addView(sourceView, Math.min(1, bubble.getChildCount()));
|
||
}
|
||
return wrapper;
|
||
}
|
||
|
||
public static LinearLayout buildForwardBundleCard(
|
||
Context context,
|
||
String senderLabel,
|
||
String cardTitle,
|
||
String summary,
|
||
@Nullable String meta,
|
||
boolean outgoing
|
||
) {
|
||
LinearLayout wrapper = new LinearLayout(context);
|
||
wrapper.setOrientation(LinearLayout.VERTICAL);
|
||
wrapper.setGravity(outgoing ? Gravity.END : Gravity.START);
|
||
LinearLayout.LayoutParams wrapperParams = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
wrapperParams.bottomMargin = dp(context, 12);
|
||
wrapper.setLayoutParams(wrapperParams);
|
||
|
||
TextView metaView = new TextView(context);
|
||
String metaText = buildMessageMetaText(senderLabel, meta, outgoing);
|
||
metaView.setText(metaText);
|
||
metaView.setTextSize(11);
|
||
metaView.setTextColor(context.getColor(R.color.boss_text_soft));
|
||
metaView.setPadding(dp(context, 6), 0, dp(context, 6), dp(context, 4));
|
||
wrapper.addView(metaView);
|
||
|
||
LinearLayout card = new LinearLayout(context);
|
||
card.setOrientation(LinearLayout.VERTICAL);
|
||
card.setMinimumWidth(dp(context, 180));
|
||
card.setPadding(dp(context, 14), dp(context, 12), dp(context, 14), dp(context, 12));
|
||
card.setBackground(createRoundedBackground(
|
||
outgoing ? Color.parseColor("#CFF0D8") : Color.WHITE,
|
||
dp(context, 18)
|
||
));
|
||
card.setElevation(dp(context, 1));
|
||
|
||
TextView kindView = new TextView(context);
|
||
kindView.setText(ProjectChatUiState.labelForForwardKind("forward_bundle"));
|
||
kindView.setTextSize(11);
|
||
kindView.setTypeface(Typeface.DEFAULT_BOLD);
|
||
kindView.setTextColor(context.getColor(R.color.boss_text_muted));
|
||
card.addView(kindView);
|
||
|
||
TextView titleView = new TextView(context);
|
||
titleView.setText(TextUtils.isEmpty(cardTitle) ? "聊天记录" : cardTitle);
|
||
titleView.setTextSize(15);
|
||
titleView.setTypeface(Typeface.DEFAULT_BOLD);
|
||
titleView.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
titleView.setPadding(0, dp(context, 6), 0, 0);
|
||
titleView.setMaxLines(2);
|
||
titleView.setEllipsize(TextUtils.TruncateAt.END);
|
||
card.addView(titleView);
|
||
|
||
TextView summaryView = new TextView(context);
|
||
summaryView.setText(TextUtils.isEmpty(summary) ? "转发的聊天记录" : summary);
|
||
summaryView.setTextSize(13);
|
||
summaryView.setLineSpacing(0f, 1.2f);
|
||
summaryView.setTextColor(context.getColor(R.color.boss_text_muted));
|
||
summaryView.setPadding(0, dp(context, 8), 0, 0);
|
||
summaryView.setMaxWidth(Math.round(context.getResources().getDisplayMetrics().widthPixels * 0.72f));
|
||
card.addView(summaryView);
|
||
|
||
wrapper.addView(card);
|
||
return wrapper;
|
||
}
|
||
|
||
public static LinearLayout buildPendingOutgoingMessageBubble(
|
||
Context context,
|
||
String senderLabel,
|
||
String body
|
||
) {
|
||
String effectiveSender = TextUtils.isEmpty(senderLabel) ? "你" : senderLabel;
|
||
return buildMessageBubble(context, effectiveSender, body, "发送中", true, null);
|
||
}
|
||
|
||
public static LinearLayout buildExecutionWarningCard(
|
||
Context context,
|
||
String title,
|
||
@Nullable String summary,
|
||
boolean outgoing
|
||
) {
|
||
LinearLayout card = new LinearLayout(context);
|
||
card.setOrientation(LinearLayout.VERTICAL);
|
||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
params.topMargin = dp(context, 8);
|
||
card.setLayoutParams(params);
|
||
card.setPadding(dp(context, 12), dp(context, 10), dp(context, 12), dp(context, 10));
|
||
card.setBackground(createRoundedBackground(
|
||
outgoing ? Color.parseColor("#FFF1D7") : Color.parseColor("#FFF7E7"),
|
||
dp(context, 14)
|
||
));
|
||
|
||
TextView titleView = new TextView(context);
|
||
titleView.setText(TextUtils.isEmpty(title) ? "执行提醒" : title);
|
||
titleView.setTextSize(12);
|
||
titleView.setTypeface(Typeface.DEFAULT_BOLD);
|
||
titleView.setTextColor(Color.parseColor("#B36B00"));
|
||
card.addView(titleView);
|
||
|
||
if (!TextUtils.isEmpty(summary)) {
|
||
TextView summaryView = new TextView(context);
|
||
summaryView.setText(summary);
|
||
summaryView.setTextSize(13);
|
||
summaryView.setLineSpacing(0f, 1.2f);
|
||
summaryView.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
summaryView.setPadding(0, dp(context, 4), 0, 0);
|
||
summaryView.setMaxWidth(Math.round(context.getResources().getDisplayMetrics().widthPixels * 0.62f));
|
||
summaryView.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY);
|
||
summaryView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
|
||
card.addView(summaryView);
|
||
}
|
||
|
||
return card;
|
||
}
|
||
|
||
public static LinearLayout buildMessageStatusRow(
|
||
Context context,
|
||
JSONObject message,
|
||
@Nullable JSONObject conversationTask,
|
||
List<JSONObject> warnings,
|
||
boolean outgoing
|
||
) {
|
||
LinearLayout row = new LinearLayout(context);
|
||
row.setOrientation(LinearLayout.VERTICAL);
|
||
LinearLayout.LayoutParams rowParams = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
rowParams.topMargin = dp(context, 8);
|
||
row.setLayoutParams(rowParams);
|
||
|
||
boolean hasTask = conversationTask != null;
|
||
boolean hasWarnings = warnings != null && !warnings.isEmpty();
|
||
String detailText = buildStatusDetailText(message, conversationTask, warnings);
|
||
boolean hasDetail = !TextUtils.isEmpty(detailText);
|
||
if (!hasTask && !hasWarnings && !hasDetail) {
|
||
row.setVisibility(View.GONE);
|
||
return row;
|
||
}
|
||
|
||
if (hasTask || hasWarnings) {
|
||
LinearLayout pillRow = new LinearLayout(context);
|
||
pillRow.setOrientation(LinearLayout.HORIZONTAL);
|
||
pillRow.setGravity(Gravity.CENTER_VERTICAL);
|
||
row.addView(pillRow);
|
||
if (hasTask) {
|
||
pillRow.addView(buildStatusPill(
|
||
context,
|
||
taskStatusLabel(conversationTask.optString("status", "")),
|
||
outgoing ? Color.parseColor("#D7F3DF") : Color.parseColor("#EDF7F0"),
|
||
Color.parseColor("#215B39")
|
||
));
|
||
}
|
||
if (hasWarnings) {
|
||
if (pillRow.getChildCount() > 0) {
|
||
View spacer = new View(context);
|
||
LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams(dp(context, 6), 1);
|
||
spacer.setLayoutParams(spacerParams);
|
||
pillRow.addView(spacer);
|
||
}
|
||
pillRow.addView(buildStatusPill(
|
||
context,
|
||
warningBadgeLabel(warnings),
|
||
outgoing ? Color.parseColor("#FFF1D7") : Color.parseColor("#FFF7E7"),
|
||
Color.parseColor("#B36B00")
|
||
));
|
||
}
|
||
}
|
||
|
||
if (hasDetail) {
|
||
TextView detailView = new TextView(context);
|
||
detailView.setText(detailText);
|
||
detailView.setTextSize(12);
|
||
detailView.setTextColor(context.getColor(R.color.boss_text_muted));
|
||
detailView.setPadding(dp(context, 2), dp(context, 4), dp(context, 2), 0);
|
||
detailView.setMaxLines(2);
|
||
detailView.setEllipsize(TextUtils.TruncateAt.END);
|
||
detailView.setMaxWidth(Math.round(context.getResources().getDisplayMetrics().widthPixels * 0.62f));
|
||
row.addView(detailView);
|
||
}
|
||
|
||
return row;
|
||
}
|
||
|
||
private static TextView buildStatusPill(
|
||
Context context,
|
||
String text,
|
||
int backgroundColor,
|
||
int textColor
|
||
) {
|
||
TextView pill = new TextView(context);
|
||
pill.setText(text);
|
||
pill.setTextSize(11);
|
||
pill.setTypeface(Typeface.DEFAULT_BOLD);
|
||
pill.setTextColor(textColor);
|
||
pill.setPadding(dp(context, 10), dp(context, 5), dp(context, 10), dp(context, 5));
|
||
pill.setBackground(createRoundedBackground(backgroundColor, dp(context, 12)));
|
||
return pill;
|
||
}
|
||
|
||
private static String taskStatusLabel(String status) {
|
||
switch (status) {
|
||
case "queued":
|
||
return "排队中";
|
||
case "running":
|
||
return "执行中";
|
||
case "completed":
|
||
return "已完成";
|
||
case "failed":
|
||
return "已失败";
|
||
default:
|
||
return TextUtils.isEmpty(status) ? "处理中" : status;
|
||
}
|
||
}
|
||
|
||
private static String warningBadgeLabel(List<JSONObject> warnings) {
|
||
int totalWarnings = 0;
|
||
for (JSONObject warning : warnings) {
|
||
if (warning == null) {
|
||
continue;
|
||
}
|
||
totalWarnings += Math.max(1, warning.optInt("count", 1));
|
||
}
|
||
return totalWarnings > 1 ? "提醒 " + totalWarnings : "执行提醒";
|
||
}
|
||
|
||
private static String buildStatusDetailText(
|
||
JSONObject message,
|
||
@Nullable JSONObject conversationTask,
|
||
List<JSONObject> warnings
|
||
) {
|
||
ArrayList<String> parts = new ArrayList<>();
|
||
if (conversationTask != null) {
|
||
String sessionId = conversationTask.optString("sessionId", "");
|
||
String taskId = conversationTask.optString("taskId", "");
|
||
if (!TextUtils.isEmpty(sessionId)) {
|
||
parts.add("Session " + sessionId);
|
||
} else if (!TextUtils.isEmpty(taskId)) {
|
||
parts.add("Task " + taskId);
|
||
}
|
||
}
|
||
if (warnings != null && !warnings.isEmpty()) {
|
||
JSONObject firstWarning = warnings.get(0);
|
||
if (firstWarning != null) {
|
||
String summary = firstWarning.optString("summary", "");
|
||
String title = firstWarning.optString("title", "执行提醒");
|
||
int count = Math.max(1, firstWarning.optInt("count", 1));
|
||
String warningText = !TextUtils.isEmpty(summary) ? summary : title;
|
||
if (count > 1) {
|
||
warningText = warningText + " 等 " + count + " 条";
|
||
} else if (warnings.size() > 1) {
|
||
warningText = warningText + " 等 " + warnings.size() + " 组";
|
||
}
|
||
parts.add(warningText);
|
||
}
|
||
} else if ("system_notice".equals(message.optString("kind", ""))) {
|
||
parts.add("系统同步提醒");
|
||
}
|
||
return TextUtils.join(" · ", parts);
|
||
}
|
||
|
||
public static void applyMessageSelectionState(Context context, View messageView, boolean selected) {
|
||
if (messageView == null) {
|
||
return;
|
||
}
|
||
if (selected) {
|
||
messageView.setBackground(createRoundedBackground(Color.parseColor("#EAF7F0"), dp(context, 20)));
|
||
messageView.setAlpha(1f);
|
||
} else {
|
||
messageView.setBackgroundColor(Color.TRANSPARENT);
|
||
messageView.setAlpha(1f);
|
||
}
|
||
}
|
||
|
||
public static TextView buildMessagePlaceholder(Context context, String text) {
|
||
TextView placeholder = new TextView(context);
|
||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
params.topMargin = dp(context, 24);
|
||
placeholder.setLayoutParams(params);
|
||
placeholder.setGravity(Gravity.CENTER);
|
||
placeholder.setPadding(dp(context, 24), dp(context, 12), dp(context, 24), dp(context, 12));
|
||
placeholder.setText(text);
|
||
placeholder.setTextSize(13);
|
||
placeholder.setTextColor(context.getColor(R.color.boss_text_soft));
|
||
return placeholder;
|
||
}
|
||
|
||
public static Button buildPrimaryButton(Context context, String label) {
|
||
Button button = new Button(context);
|
||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
params.topMargin = dp(context, 12);
|
||
button.setLayoutParams(params);
|
||
button.setText(label);
|
||
button.setTextColor(context.getColor(R.color.boss_surface));
|
||
button.setBackgroundResource(R.drawable.bg_primary_button);
|
||
button.setPadding(dp(context, 14), dp(context, 12), dp(context, 14), dp(context, 12));
|
||
button.setAllCaps(false);
|
||
return button;
|
||
}
|
||
|
||
public static Button buildSecondaryButton(Context context, String label) {
|
||
Button button = new Button(context);
|
||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
params.topMargin = dp(context, 10);
|
||
button.setLayoutParams(params);
|
||
button.setText(label);
|
||
button.setTextColor(context.getColor(R.color.boss_green));
|
||
button.setBackgroundResource(R.drawable.bg_secondary_button);
|
||
button.setPadding(dp(context, 14), dp(context, 12), dp(context, 14), dp(context, 12));
|
||
button.setAllCaps(false);
|
||
return button;
|
||
}
|
||
|
||
private static LinearLayout buildMessageWrapper(
|
||
Context context,
|
||
String senderLabel,
|
||
@Nullable String meta,
|
||
boolean outgoing
|
||
) {
|
||
LinearLayout wrapper = new LinearLayout(context);
|
||
wrapper.setOrientation(LinearLayout.VERTICAL);
|
||
wrapper.setGravity(outgoing ? Gravity.END : Gravity.START);
|
||
LinearLayout.LayoutParams wrapperParams = new LinearLayout.LayoutParams(
|
||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||
);
|
||
wrapperParams.bottomMargin = dp(context, 12);
|
||
wrapper.setLayoutParams(wrapperParams);
|
||
|
||
TextView metaView = new TextView(context);
|
||
String metaText = buildMessageMetaText(senderLabel, meta, outgoing);
|
||
metaView.setText(metaText);
|
||
metaView.setTextSize(11);
|
||
metaView.setTextColor(context.getColor(R.color.boss_text_soft));
|
||
metaView.setPadding(dp(context, 6), 0, dp(context, 6), dp(context, 4));
|
||
wrapper.addView(metaView);
|
||
return wrapper;
|
||
}
|
||
|
||
private static String buildMessageMetaText(
|
||
String senderLabel,
|
||
@Nullable String meta,
|
||
boolean outgoing
|
||
) {
|
||
if (outgoing && !TextUtils.isEmpty(meta)) {
|
||
return meta;
|
||
}
|
||
if (TextUtils.isEmpty(meta)) {
|
||
return senderLabel;
|
||
}
|
||
if (TextUtils.isEmpty(senderLabel)) {
|
||
return meta;
|
||
}
|
||
return senderLabel + " · " + meta;
|
||
}
|
||
|
||
private static TextView buildAttachmentPrimaryText(Context context, String text) {
|
||
TextView primary = new TextView(context);
|
||
primary.setText(TextUtils.isEmpty(text) ? "未命名附件" : text);
|
||
primary.setTextSize(15);
|
||
primary.setTypeface(Typeface.DEFAULT_BOLD);
|
||
primary.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
primary.setMaxLines(2);
|
||
primary.setEllipsize(TextUtils.TruncateAt.END);
|
||
return primary;
|
||
}
|
||
|
||
private static TextView buildAttachmentSecondaryText(Context context, String text) {
|
||
TextView secondary = new TextView(context);
|
||
secondary.setText(TextUtils.isEmpty(text) ? "已发送" : text);
|
||
secondary.setTextSize(13);
|
||
secondary.setTextColor(context.getColor(R.color.boss_text_muted));
|
||
secondary.setPadding(0, dp(context, 6), 0, 0);
|
||
return secondary;
|
||
}
|
||
|
||
private static String joinAttachmentDetail(@Nullable String detail, @Nullable String status) {
|
||
if (TextUtils.isEmpty(detail)) {
|
||
return TextUtils.isEmpty(status) ? "已发送" : status;
|
||
}
|
||
if (TextUtils.isEmpty(status)) {
|
||
return detail;
|
||
}
|
||
return detail + " · " + status;
|
||
}
|
||
|
||
public static EditText buildInput(Context context, String hint, boolean multiline) {
|
||
EditText input = new EditText(context);
|
||
input.setHint(hint);
|
||
input.setTextColor(context.getColor(R.color.boss_text_primary));
|
||
input.setHintTextColor(context.getColor(R.color.boss_text_muted));
|
||
input.setBackgroundResource(R.drawable.bg_secondary_button);
|
||
input.setPadding(dp(context, 14), dp(context, 12), dp(context, 14), dp(context, 12));
|
||
input.setSingleLine(!multiline);
|
||
if (multiline) {
|
||
input.setMinLines(3);
|
||
input.setMaxLines(6);
|
||
}
|
||
return input;
|
||
}
|
||
|
||
public static void copyText(Context context, String label, String text) {
|
||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||
if (clipboard != null) {
|
||
clipboard.setPrimaryClip(ClipData.newPlainText(label, text));
|
||
Toast.makeText(context, label + " 已复制", Toast.LENGTH_SHORT).show();
|
||
}
|
||
}
|
||
|
||
public static int dp(Context context, int value) {
|
||
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(
|
||
context,
|
||
firstNonEmpty(row.avatarPrimary, row.threadTitle, "会"),
|
||
AVATAR_BG_COLORS[0],
|
||
Color.WHITE,
|
||
52
|
||
);
|
||
}
|
||
|
||
FrameLayout groupWrap = new FrameLayout(context);
|
||
LinearLayout.LayoutParams wrapParams = new LinearLayout.LayoutParams(dp(context, 52), dp(context, 52));
|
||
groupWrap.setLayoutParams(wrapParams);
|
||
|
||
GradientDrawable bg = createRoundedBackground(Color.parseColor("#F2F6F3"), dp(context, 18));
|
||
bg.setStroke(dp(context, 1), Color.parseColor("#E5ECE7"));
|
||
groupWrap.setBackground(bg);
|
||
|
||
int visibleCount = Math.min(row.groupAvatarMembers.length, 4);
|
||
if (visibleCount == 0) {
|
||
groupWrap.addView(buildCenteredAvatarTile(context, "群", AVATAR_BG_COLORS[1], 32, 10, 10));
|
||
return groupWrap;
|
||
}
|
||
|
||
int[][] offsets = {
|
||
{4, 4},
|
||
{24, 4},
|
||
{4, 24},
|
||
{24, 24}
|
||
};
|
||
for (int i = 0; i < visibleCount; i++) {
|
||
WechatSurfaceMapper.GroupAvatarMember member = row.groupAvatarMembers[i];
|
||
groupWrap.addView(buildCenteredAvatarTile(
|
||
context,
|
||
firstNonEmpty(member.avatarLabel, member.title, "群"),
|
||
AVATAR_BG_COLORS[(i + 1) % AVATAR_BG_COLORS.length],
|
||
22,
|
||
offsets[i][0],
|
||
offsets[i][1]
|
||
));
|
||
}
|
||
return groupWrap;
|
||
}
|
||
|
||
private static TextView buildAvatarCircle(
|
||
Context context,
|
||
String labelSource,
|
||
int backgroundColor,
|
||
int textColor,
|
||
int sizeDp
|
||
) {
|
||
TextView avatarView = new TextView(context);
|
||
LinearLayout.LayoutParams avatarParams = new LinearLayout.LayoutParams(dp(context, sizeDp), dp(context, sizeDp));
|
||
avatarView.setLayoutParams(avatarParams);
|
||
avatarView.setGravity(Gravity.CENTER);
|
||
avatarView.setText(firstLetter(labelSource));
|
||
avatarView.setTextSize(sizeDp >= 48 ? 24 : 12);
|
||
avatarView.setTypeface(Typeface.DEFAULT_BOLD);
|
||
avatarView.setTextColor(textColor);
|
||
avatarView.setBackground(createRoundedBackground(backgroundColor, dp(context, sizeDp / 2)));
|
||
return avatarView;
|
||
}
|
||
|
||
private static View buildCenteredAvatarTile(
|
||
Context context,
|
||
String labelSource,
|
||
int backgroundColor,
|
||
int sizeDp,
|
||
int leftDp,
|
||
int topDp
|
||
) {
|
||
TextView tile = buildAvatarCircle(context, labelSource, backgroundColor, context.getColor(R.color.boss_text_primary), sizeDp);
|
||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(dp(context, sizeDp), dp(context, sizeDp));
|
||
params.leftMargin = dp(context, leftDp);
|
||
params.topMargin = dp(context, topDp);
|
||
tile.setLayoutParams(params);
|
||
return tile;
|
||
}
|
||
|
||
private static GradientDrawable createRoundedBackground(int color, int radiusPx) {
|
||
GradientDrawable drawable = new GradientDrawable();
|
||
drawable.setColor(color);
|
||
drawable.setCornerRadius(radiusPx);
|
||
return drawable;
|
||
}
|
||
|
||
private static int resolveDeviceAccentColor(String statusKey) {
|
||
if ("online".equals(statusKey)) {
|
||
return DEVICE_STATUS_ONLINE;
|
||
}
|
||
if ("abnormal".equals(statusKey)) {
|
||
return DEVICE_STATUS_ABNORMAL;
|
||
}
|
||
return DEVICE_STATUS_OFFLINE;
|
||
}
|
||
|
||
private static int resolveDeviceAvatarBackground(String statusKey) {
|
||
if ("online".equals(statusKey)) {
|
||
return Color.parseColor("#E5F6EC");
|
||
}
|
||
if ("abnormal".equals(statusKey)) {
|
||
return Color.parseColor("#FCE9E7");
|
||
}
|
||
return Color.parseColor("#F2F4F7");
|
||
}
|
||
|
||
private static int resolveDeviceMetaColor(Context context, String statusKey) {
|
||
if ("online".equals(statusKey)) {
|
||
return context.getColor(R.color.boss_text_muted);
|
||
}
|
||
return resolveDeviceAccentColor(statusKey);
|
||
}
|
||
|
||
private static View buildAnimatedActivityDot(Context context, int index) {
|
||
View dot = new View(context);
|
||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(dp(context, 7), dp(context, 7));
|
||
dot.setLayoutParams(params);
|
||
|
||
GradientDrawable drawable = createRoundedBackground(
|
||
ACTIVITY_ICON_COLORS[index % ACTIVITY_ICON_COLORS.length],
|
||
dp(context, 4)
|
||
);
|
||
dot.setBackground(drawable);
|
||
dot.setAlpha(0.92f - (Math.min(index, 2) * 0.12f));
|
||
dot.setScaleX(1f);
|
||
dot.setScaleY(1f);
|
||
return dot;
|
||
}
|
||
|
||
private static int resolveConversationContextColor(Context context, @Nullable String level) {
|
||
if (TextUtils.isEmpty(level)) {
|
||
return context.getColor(R.color.boss_text_soft);
|
||
}
|
||
switch (level) {
|
||
case "critical":
|
||
return Color.parseColor("#D94B4B");
|
||
case "urgent":
|
||
return Color.parseColor("#E0832A");
|
||
case "watch":
|
||
return Color.parseColor("#7B8A82");
|
||
default:
|
||
return context.getColor(R.color.boss_text_soft);
|
||
}
|
||
}
|
||
|
||
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, 2f);
|
||
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 / 2f + dp(context, 0.5f);
|
||
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()) {
|
||
return "会";
|
||
}
|
||
return text.substring(0, 1);
|
||
}
|
||
|
||
private static String firstNonEmpty(String first, String second, String fallback) {
|
||
if (!TextUtils.isEmpty(first)) {
|
||
return first;
|
||
}
|
||
if (!TextUtils.isEmpty(second)) {
|
||
return second;
|
||
}
|
||
return fallback;
|
||
}
|
||
}
|