diff --git a/main/plugins/Group Monitoring/README.md b/main/plugins/Group Monitoring/README.md
new file mode 100644
index 0000000..0120fd2
--- /dev/null
+++ b/main/plugins/Group Monitoring/README.md
@@ -0,0 +1,104 @@
+# 群成员监控插件 v2.0
+
+实时监控群聊成员变动,有成员加入或退出时自动通知。
+
+## 功能特性
+
+- 🔍 **实时监控**:基于系统消息,即时检测成员变动
+- 📥 **进群监控**:成员加入时发送欢迎消息
+- 📤 **退群监控**:成员退出时发送通知
+- 📝 **自定义消息**:可自定义欢迎语和退群通知语
+- 🎛️ **可视化控制面板**:选择要监控的群聊
+
+## 使用方法
+
+### 打开控制面板
+
+在微信聊天界面长按消息,选择 **群监控** 菜单。
+
+### 面板功能
+
+| 功能 | 说明 |
+|------|------|
+| 启用监控 | 开启/关闭总开关 |
+| 退群监控 | 开启后,有成员退群时发送通知 |
+| 进群监控 | 开启后,有成员进群时发送欢迎消息 |
+| 消息设置 | 自定义欢迎语和退群通知语 |
+| 群列表 | 显示所有群聊,可单独开启/关闭监控 |
+
+### 消息模板
+
+支持变量替换:
+
+| 变量 | 说明 | 示例 |
+|------|------|------|
+| `@name` | 成员的群昵称 | `欢迎 @name 加入群聊!` |
+
+## 配置说明
+
+| 配置项 | 默认值 | 说明 |
+|--------|--------|------|
+| `gm_enabled` | false | 总开关 |
+| `gm_watch_{群ID}` | false | 单个群的监控开关 |
+| `gm_notify` | true | 退群监控开关 |
+| `gm_at_join` | false | 进群监控开关 |
+| `gm_welcome` | `欢迎 @name 加入群聊!` | 欢迎语模板 |
+| `gm_leave` | `@name 退出了群聊` | 退群通知语模板 |
+
+## 文件结构
+
+```
+群成员监控/
+├── info.prop # 插件元数据
+├── main.java # 入口 + 消息监听
+├── README.md # 使用文档
+├── UI开发文档.md # UI 开发参考
+└── views/
+ ├── helper.java # 核心逻辑
+ └── ui.java # UI 面板
+```
+
+## 技术实现
+
+### 消息格式
+
+成员变动时,微信会发送系统消息,包含XML格式数据:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### 类型判断
+
+| content_template type | 场景 |
+|----------------------|------|
+| `tmpl_type_profile` | 移出群聊 |
+| `tmpl_type_profilewithrevoke` | 邀请加入 |
+
+### 昵称获取
+
+使用 `getUserName(groupId, wxid)` 获取群内昵称:
+- 优先显示群昵称
+- 如果没有设置群昵称,显示备注或微信昵称
+
+## 注意事项
+
+- 已退出的群聊不会显示在列表中
+- 需要重新加载插件才能生效
+- 静默运行,无日志输出
diff --git a/main/plugins/Group Monitoring/info.prop b/main/plugins/Group Monitoring/info.prop
new file mode 100644
index 0000000..6c54f1b
--- /dev/null
+++ b/main/plugins/Group Monitoring/info.prop
@@ -0,0 +1,4 @@
+name=群成员监控
+author=Operit
+version=2.0.0
+desc=监控指定群聊的成员变化,UI面板控制开关
diff --git a/main/plugins/Group Monitoring/main.java b/main/plugins/Group Monitoring/main.java
new file mode 100644
index 0000000..8d73395
--- /dev/null
+++ b/main/plugins/Group Monitoring/main.java
@@ -0,0 +1,49 @@
+// ==================== 群成员监控 v2.0.0 ====================
+// 实时监控:基于系统消息检测进退群
+
+String KEY_ENABLED = "gm_enabled"
+String PRE = "gm_watch_"
+String KEY_NOTIFY = "gm_notify"
+String KEY_AT_JOIN = "gm_at_join"
+
+onLoad() {
+ if (!configContains(KEY_ENABLED)) {
+ setBoolean(KEY_ENABLED, false)
+ setBoolean(KEY_NOTIFY, true)
+ setBoolean(KEY_AT_JOIN, false)
+ }
+ loadJava("views/helper")
+ loadJava("views/ui")
+}
+
+onUnload() {
+}
+
+void onMsg(Object msg) {
+ String content = msg.content
+ if (content == null || content.isEmpty()) return
+
+ // 检查是否包含群成员变动的 XML 特征
+ if (!content.contains("sysmsgtemplate")) return
+
+ if (!msg.isGroupChat()) return
+
+ // 检查总开关
+ boolean enabled = getBoolean(KEY_ENABLED, false)
+ boolean groupWatched = getBoolean(PRE + msg.talker, false)
+
+ if (!enabled) {
+ return
+ }
+
+ if (!groupWatched) {
+ return
+ }
+
+ // 处理成员变动
+ handleMemberChange(msg.talker, content)
+}
+
+void onMsgMenu(Object msg) {
+ addMenuItem("群监控", "", () -> { showMainPanel(); })
+}
diff --git a/main/plugins/Group Monitoring/views/helper.java b/main/plugins/Group Monitoring/views/helper.java
new file mode 100644
index 0000000..edb1e2b
--- /dev/null
+++ b/main/plugins/Group Monitoring/views/helper.java
@@ -0,0 +1,128 @@
+// ==================== helper.java - 实时监控核心 ====================
+
+// 处理成员变动
+void handleMemberChange(String gid, String content) {
+ if (!getBoolean(PRE + gid, false)) return
+
+ // 提取 wxid
+ String wxid = extractWxid(content)
+ if (wxid == null || wxid.isEmpty()) return
+
+ // 提取昵称
+ String nickname = extractNickname(content)
+
+ // 判断加入/退出
+ boolean isJoin = content.contains("tmpl_type_profilewithrevoke")
+ boolean isLeave = content.contains("tmpl_type_profile") && !isJoin
+
+ if (isJoin) {
+ handleJoin(gid, wxid, nickname)
+ } else if (isLeave) {
+ handleLeave(gid, wxid, nickname)
+ }
+}
+
+// 处理加入
+void handleJoin(String gid, String wxid, String nickname) {
+ // 获取群昵称
+ String name = getUserName(gid, wxid)
+ if (name == null || name.isEmpty()) {
+ name = nickname // 备用
+ }
+ // 进群监控开启时,发送欢迎消息
+ if (getBoolean(KEY_AT_JOIN, false)) {
+ String welcome = getString("gm_welcome", "欢迎 @name 加入群聊!")
+ String msg = welcome.replace("@name", name)
+ sendText(gid, msg)
+ }
+
+ setLong("gm_last", System.currentTimeMillis() / 1000)
+}
+
+// 处理退出
+void handleLeave(String gid, String wxid, String nickname) {
+ // 获取群昵称
+ String name = getUserName(gid, wxid)
+ if (name == null || name.isEmpty()) {
+ name = nickname // 备用
+ }
+ // 退群监控开启时,发送通知
+ if (getBoolean(KEY_NOTIFY, false)) {
+ String leaveMsg = getString("gm_leave", "@name 退出了群聊")
+ String msg = leaveMsg.replace("@name", name)
+ sendText(gid, msg)
+ }
+
+ setLong("gm_last", System.currentTimeMillis() / 1000)
+}
+
+// 从 XML 提取 wxid
+String extractWxid(String xml) {
+ try {
+ // 匹配 revokemsg 中的 wxid
+ int idx = xml.indexOf("")
+ if (idx > 0) {
+ int start = idx + 14
+ int end = xml.indexOf("", start)
+ if (end > start) {
+ return xml.substring(start, end)
+ }
+ }
+
+ // 匹配 profilewithrevoke 中的 wxid
+ idx = xml.indexOf("wxid_")
+ if (idx >= 0) {
+ int end = idx
+ while (end < xml.length()) {
+ char c = xml.charAt(end)
+ if (c == '<' || c == '&' || c == ' ' || c == '"') break
+ end++
+ }
+ return xml.substring(idx, end)
+ }
+ } catch (Exception e) {
+ }
+ return null
+}
+
+// 从 XML 提取昵称
+String extractNickname(String xml) {
+ try {
+ // 查找 nickname 标签内的 CDATA
+ int nickIdx = xml.indexOf("")
+ if (nickIdx >= 0) {
+ int cdataStart = xml.indexOf("= 0 && cdataStart - nickIdx < 20) {
+ int start = cdataStart + 9 // "", start)
+ if (end > start) {
+ String name = xml.substring(start, end).trim()
+ if (!name.isEmpty()) {
+ return name
+ }
+ }
+ }
+
+ // 如果没有 CDATA,尝试普通内容
+ int start = nickIdx + 10
+ int end = xml.indexOf("", start)
+ if (end > start) {
+ String name = xml.substring(start, end).trim()
+ if (!name.isEmpty()) {
+ return name
+ }
+ }
+ }
+ } catch (Exception e) {
+ }
+ return "未知用户"
+}
+
+// 获取成员名称(优先用getUserName)
+String getMemberName(String gid, String wxid, String fallback) {
+ String name = getUserName(gid, wxid)
+ if (name == null || name.isEmpty()) {
+ return fallback
+ }
+ return name
+}
diff --git a/main/plugins/Group Monitoring/views/ui.java b/main/plugins/Group Monitoring/views/ui.java
new file mode 100644
index 0000000..891d953
--- /dev/null
+++ b/main/plugins/Group Monitoring/views/ui.java
@@ -0,0 +1,629 @@
+// ==================== ui.java - 群成员监控面板 ====================
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.GradientDrawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.TranslateAnimation;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+// 颜色常量
+final String C_GREEN = "#07C160";
+final String C_RED = "#FA5151";
+final String C_GRAY = "#EAEAEA";
+final String C_BLUE = "#576B95";
+final String C_SUB = "#888888";
+
+Dialog _dlg = null;
+
+// 工具方法:生成圆角背景
+GradientDrawable createRoundRect(String hexColor, float radius) {
+ GradientDrawable drawable = new GradientDrawable();
+ drawable.setColor(Color.parseColor(hexColor));
+ drawable.setCornerRadius(radius);
+ return drawable;
+}
+
+// 创建入场动画
+AnimationSet createEnterAnimation() {
+ AnimationSet animSet = new AnimationSet(true);
+ animSet.setDuration(250);
+ TranslateAnimation slide = new TranslateAnimation(
+ Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0,
+ Animation.RELATIVE_TO_SELF, 0.3f, Animation.RELATIVE_TO_SELF, 0
+ );
+ slide.setDuration(250);
+ animSet.addAnimation(slide);
+ AlphaAnimation alpha = new AlphaAnimation(0, 1);
+ alpha.setDuration(250);
+ animSet.addAnimation(alpha);
+ return animSet;
+}
+
+// 安全获取群列表(过滤已退出的群)
+java.util.List getGroupsSafe() {
+ java.util.List gs = getGroupList();
+ if (gs == null || gs.isEmpty()) return null;
+
+ // 获取当前用户wxid
+ String myWxid = getMyWxId();
+
+ java.util.List result = new java.util.ArrayList();
+ for (int i = 0; i < gs.size(); i++) {
+ java.util.Map g = (java.util.Map) gs.get(i);
+ String gid = (String) g.get("username");
+ if (gid == null) continue;
+
+ // 检查是否还在群里
+ if (myWxid != null) {
+ java.util.List members = getGroupMemberList(gid);
+ if (members == null || !members.contains(myWxid)) continue;
+ }
+
+ result.add(g);
+ }
+
+ if (result.isEmpty()) return null;
+ return result;
+}
+
+// 统计监控中的群数量
+int countActiveGroups() {
+ java.util.List gs = getGroupsSafe();
+ int n = 0;
+ if (gs != null) {
+ for (int i = 0; i < gs.size(); i++) {
+ java.util.Map g = (java.util.Map) gs.get(i);
+ if (getBoolean("gm_watch_" + (String) g.get("username"), false)) n++;
+ }
+ }
+ return n;
+}
+
+// 同步总开关
+void syncMasterSwitch() {
+ setBoolean("gm_enabled", countActiveGroups() > 0);
+}
+
+// 关闭旧弹窗
+void dismissOld() {
+ if (_dlg != null && _dlg.isShowing()) {
+ try { _dlg.dismiss(); } catch (Exception e) {}
+ }
+}
+
+// ==================== 主面板入口 ====================
+void showMainPanel() {
+ Activity context = getTopActivity();
+ if (context == null) {
+ toast("请在微信主界面操作");
+ return;
+ }
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ public void run() {
+ buildAndShowDialog(context);
+ }
+ });
+}
+
+// ==================== 构建UI ====================
+void buildAndShowDialog(Activity context) {
+ try {
+ dismissOld();
+
+ boolean running = getBoolean("gm_enabled", false);
+ int ac = countActiveGroups();
+
+ // 外层容器
+ FrameLayout rootFrame = new FrameLayout(context);
+ rootFrame.setPadding(50, 50, 50, 50);
+
+ // 主面板卡片
+ LinearLayout mainPanel = new LinearLayout(context);
+ mainPanel.setOrientation(LinearLayout.VERTICAL);
+ mainPanel.setBackground(createRoundRect("#F7F7F7", 40f));
+ mainPanel.setPadding(60, 60, 60, 60);
+ mainPanel.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ ));
+
+ // 标题行
+ LinearLayout headerRow = new LinearLayout(context);
+ headerRow.setOrientation(LinearLayout.HORIZONTAL);
+ headerRow.setGravity(Gravity.CENTER_VERTICAL);
+ headerRow.setPadding(0, 0, 0, 40);
+
+ TextView titleView = new TextView(context);
+ titleView.setText("群成员监控");
+ titleView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
+ titleView.setTextColor(Color.parseColor("#111111"));
+ titleView.getPaint().setFakeBoldText(true);
+ LinearLayout.LayoutParams titleParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f);
+ titleView.setLayoutParams(titleParams);
+ headerRow.addView(titleView);
+
+ // 状态标签
+ TextView badge = new TextView(context);
+ badge.setText(running ? "运行中·" + ac + "个群" : "已停止");
+ badge.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
+ badge.setTextColor(Color.WHITE);
+ badge.setBackground(createRoundRect(running ? C_GREEN : C_SUB, 20f));
+ badge.setPadding(30, 10, 30, 10);
+ headerRow.addView(badge);
+ mainPanel.addView(headerRow);
+
+ // 滚动区域
+ ScrollView scrollView = new ScrollView(context);
+ scrollView.setVerticalScrollBarEnabled(false);
+ LinearLayout.LayoutParams scrollParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, 850
+ );
+ scrollView.setLayoutParams(scrollParams);
+
+ LinearLayout listContainer = new LinearLayout(context);
+ listContainer.setOrientation(LinearLayout.VERTICAL);
+ scrollView.addView(listContainer);
+
+ // ===== 群列表区背景 =====
+ LinearLayout listSection = new LinearLayout(context);
+ listSection.setOrientation(LinearLayout.VERTICAL);
+ listSection.setBackground(createRoundRect("#F0F4F8", 20f));
+ listSection.setPadding(25, 25, 25, 25);
+ LinearLayout.LayoutParams listSectionParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
+ );
+ listSectionParams.setMargins(0, 0, 0, 20);
+ listSection.setLayoutParams(listSectionParams);
+
+ // 群列表标题
+ TextView listTitle = new TextView(context);
+ listTitle.setText("监控群列表");
+ listTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
+ listTitle.setTextColor(Color.parseColor("#999999"));
+ listTitle.setPadding(5, 0, 0, 15);
+ listSection.addView(listTitle);
+
+ // ===== 群列表 =====
+ java.util.List groups = getGroupsSafe();
+ if (groups != null && !groups.isEmpty()) {
+ for (int i = 0; i < groups.size(); i++) {
+ final java.util.Map g = (java.util.Map) groups.get(i);
+ final String gid = (String) g.get("username");
+ String nick = (String) g.get("nickname");
+ if (gid == null) continue;
+
+ boolean watched = getBoolean("gm_watch_" + gid, false);
+ String displayName = (nick != null && nick.length() > 0) ? nick : gid;
+
+ // 群卡片
+ LinearLayout itemCard = new LinearLayout(context);
+ itemCard.setOrientation(LinearLayout.HORIZONTAL);
+ itemCard.setGravity(Gravity.CENTER_VERTICAL);
+ itemCard.setBackground(createRoundRect("#FFFFFF", 24f));
+ itemCard.setPadding(40, 30, 40, 30);
+ LinearLayout.LayoutParams itemParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
+ );
+ itemParams.setMargins(0, 0, 0, 20);
+ itemCard.setLayoutParams(itemParams);
+
+ // 群名
+ TextView nameText = new TextView(context);
+ nameText.setText(displayName);
+ nameText.setTextColor(Color.parseColor("#333333"));
+ nameText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
+ nameText.setSingleLine(true);
+ LinearLayout.LayoutParams nameParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f);
+ nameText.setLayoutParams(nameParams);
+ itemCard.addView(nameText);
+
+ // 监控状态文字
+ TextView statusText = new TextView(context);
+ statusText.setText(watched ? "监控中" : "未监控");
+ statusText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
+ statusText.setTextColor(Color.parseColor(watched ? C_GREEN : C_SUB));
+ statusText.setPadding(0, 0, 20, 0);
+ itemCard.addView(statusText);
+
+ // 切换按钮
+ Button toggleBtn = new Button(context);
+ toggleBtn.setText(watched ? "开启" : "关闭");
+ toggleBtn.setTextColor(watched ? Color.WHITE : Color.parseColor(C_SUB));
+ toggleBtn.setBackground(createRoundRect(watched ? C_GREEN : C_GRAY, 16f));
+ toggleBtn.setPadding(30, 0, 30, 0);
+ toggleBtn.setTag(gid);
+ toggleBtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ String id = (String) v.getTag();
+ boolean cur = getBoolean("gm_watch_" + id, false);
+ boolean newVal = !cur;
+ setBoolean("gm_watch_" + id, newVal);
+ syncMasterSwitch();
+ toast(newVal ? "已开启监控" : "已关闭监控");
+ dismissOld();
+ showMainPanel();
+ }
+ });
+ itemCard.addView(toggleBtn);
+ listSection.addView(itemCard);
+ }
+ } else {
+ TextView emptyText = new TextView(context);
+ emptyText.setText("暂无群聊");
+ emptyText.setTextColor(Color.parseColor("#999999"));
+ emptyText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
+ emptyText.setGravity(Gravity.CENTER);
+ emptyText.setPadding(0, 30, 0, 30);
+ listSection.addView(emptyText);
+ }
+
+ listContainer.addView(listSection);
+ mainPanel.addView(scrollView);
+
+ // ===== 功能区背景 =====
+ LinearLayout funcSection = new LinearLayout(context);
+ funcSection.setOrientation(LinearLayout.VERTICAL);
+ funcSection.setBackground(createRoundRect("#F8F8F8", 20f));
+ funcSection.setPadding(25, 25, 25, 25);
+ LinearLayout.LayoutParams funcParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
+ );
+ funcParams.setMargins(0, 0, 0, 25);
+ funcSection.setLayoutParams(funcParams);
+
+ // 启用监控
+ LinearLayout masterCard = new LinearLayout(context);
+ masterCard.setOrientation(LinearLayout.HORIZONTAL);
+ masterCard.setGravity(Gravity.CENTER_VERTICAL);
+ masterCard.setBackground(createRoundRect("#FFFFFF", 16f));
+ masterCard.setPadding(35, 28, 35, 28);
+ LinearLayout.LayoutParams masterParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
+ );
+ masterParams.setMargins(0, 0, 0, 15);
+ masterCard.setLayoutParams(masterParams);
+
+ TextView masterLabel = new TextView(context);
+ masterLabel.setText("启用监控");
+ masterLabel.setTextColor(Color.parseColor("#333333"));
+ masterLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
+ LinearLayout.LayoutParams mlParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f);
+ masterLabel.setLayoutParams(mlParams);
+ masterCard.addView(masterLabel);
+
+ Button masterBtn = new Button(context);
+ masterBtn.setText(running ? "运行中" : "已停止");
+ masterBtn.setTextColor(running ? Color.WHITE : Color.parseColor("#999999"));
+ masterBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
+ masterBtn.setBackground(createRoundRect(running ? C_GREEN : C_GRAY, 18f));
+ masterBtn.setPadding(25, 10, 25, 10);
+ masterBtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ boolean cur = getBoolean("gm_enabled", false);
+ setBoolean("gm_enabled", !cur);
+ toast(!cur ? "监控已开启" : "监控已关闭");
+ dismissOld();
+ showMainPanel();
+ }
+ });
+ masterCard.addView(masterBtn);
+ funcSection.addView(masterCard);
+
+ // 退群+进群 并排
+ LinearLayout btnRow = new LinearLayout(context);
+ btnRow.setOrientation(LinearLayout.HORIZONTAL);
+ LinearLayout.LayoutParams btnRowParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
+ );
+ btnRowParams.setMargins(0, 0, 0, 15);
+ btnRow.setLayoutParams(btnRowParams);
+
+ boolean notifyOn = getBoolean("gm_notify", true);
+ boolean atJoinOn = getBoolean("gm_at_join", false);
+
+ // 退群监控按钮
+ Button notifyBtn = new Button(context);
+ notifyBtn.setText("退群监控");
+ notifyBtn.setTextColor(notifyOn ? Color.WHITE : Color.parseColor("#888888"));
+ notifyBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
+ notifyBtn.setBackground(createRoundRect(notifyOn ? C_RED : "#EEEEEE", 18f));
+ notifyBtn.setPadding(20, 22, 20, 22);
+ LinearLayout.LayoutParams notifyBtnParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f);
+ notifyBtnParams.setMargins(0, 0, 8, 0);
+ notifyBtn.setLayoutParams(notifyBtnParams);
+ notifyBtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ boolean cur = getBoolean("gm_notify", true);
+ setBoolean("gm_notify", !cur);
+ toast(!cur ? "退群监控已开启" : "退群监控已关闭");
+ dismissOld();
+ showMainPanel();
+ }
+ });
+ btnRow.addView(notifyBtn);
+
+ // 进群监控按钮
+ Button atBtn = new Button(context);
+ atBtn.setText("进群监控");
+ atBtn.setTextColor(atJoinOn ? Color.WHITE : Color.parseColor("#888888"));
+ atBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
+ atBtn.setBackground(createRoundRect(atJoinOn ? C_GREEN : "#EEEEEE", 18f));
+ atBtn.setPadding(20, 22, 20, 22);
+ LinearLayout.LayoutParams atBtnParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f);
+ atBtnParams.setMargins(8, 0, 0, 0);
+ atBtn.setLayoutParams(atBtnParams);
+ atBtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ boolean cur = getBoolean("gm_at_join", false);
+ setBoolean("gm_at_join", !cur);
+ toast(!cur ? "进群监控已开启" : "进群监控已关闭");
+ dismissOld();
+ showMainPanel();
+ }
+ });
+ btnRow.addView(atBtn);
+ funcSection.addView(btnRow);
+
+ // 消息设置
+ LinearLayout settingsCard = new LinearLayout(context);
+ settingsCard.setOrientation(LinearLayout.HORIZONTAL);
+ settingsCard.setGravity(Gravity.CENTER_VERTICAL);
+ settingsCard.setBackground(createRoundRect("#FFFFFF", 16f));
+ settingsCard.setPadding(35, 28, 35, 28);
+
+ TextView settingsLabel = new TextView(context);
+ settingsLabel.setText("消息设置");
+ settingsLabel.setTextColor(Color.parseColor("#333333"));
+ settingsLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
+ LinearLayout.LayoutParams slParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f);
+ settingsLabel.setLayoutParams(slParams);
+ settingsCard.addView(settingsLabel);
+
+ Button settingsBtn = new Button(context);
+ settingsBtn.setText("设置 >");
+ settingsBtn.setTextColor(Color.WHITE);
+ settingsBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
+ settingsBtn.setBackground(createRoundRect(C_BLUE, 18f));
+ settingsBtn.setPadding(25, 10, 25, 10);
+ settingsBtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ dismissOld();
+ showWelcomePanel(context);
+ }
+ });
+ settingsCard.addView(settingsBtn);
+ funcSection.addView(settingsCard);
+
+ mainPanel.addView(funcSection);
+
+ // ===== 检测时间 =====
+ long lastTime = getLong("gm_last", 0);
+ String timeStr = "未检测";
+ if (lastTime > 0) {
+ try {
+ java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("MM-dd HH:mm:ss");
+ timeStr = sdf.format(new java.util.Date(lastTime * 1000));
+ } catch (Exception e) {
+ timeStr = String.valueOf(lastTime);
+ }
+ }
+
+ TextView timeView = new TextView(context);
+ timeView.setText("最后检测: " + timeStr);
+ timeView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
+ timeView.setTextColor(Color.parseColor(C_SUB));
+ timeView.setGravity(Gravity.CENTER);
+ timeView.setPadding(0, 20, 0, 20);
+ mainPanel.addView(timeView);
+
+ // 保存并关闭按钮
+ Button closeBtn = new Button(context);
+ closeBtn.setText("保存并关闭");
+ closeBtn.setTextColor(Color.WHITE);
+ closeBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
+ closeBtn.setBackground(createRoundRect(C_BLUE, 24f));
+ LinearLayout.LayoutParams closeParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, 120
+ );
+ closeParams.setMargins(0, 30, 0, 0);
+ closeBtn.setLayoutParams(closeParams);
+ mainPanel.addView(closeBtn);
+
+ rootFrame.addView(mainPanel);
+
+ // 创建弹窗
+ _dlg = new AlertDialog.Builder(context).setView(rootFrame).create();
+
+ closeBtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ dismissOld();
+ }
+ });
+
+ // 透明背景 + 正方形
+ Window window = _dlg.getWindow();
+ if (window != null) {
+ window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ int sz = context.getResources().getDisplayMetrics().widthPixels;
+ window.setLayout(sz, sz);
+ }
+ _dlg.show();
+
+ // 应用入场动画
+ rootFrame.startAnimation(createEnterAnimation());
+
+ } catch (Exception e) {
+ log("[群监控] UI异常: " + e.getMessage());
+ toast("界面加载失败");
+ }
+}
+
+// ==================== 消息设置面板 ====================
+void showWelcomePanel(Activity context) {
+ try {
+ dismissOld();
+
+ String currentWelcome = getString("gm_welcome", "欢迎 @name 加入群聊!");
+ String currentLeave = getString("gm_leave", "@name 退出了群聊");
+
+ // 外层容器
+ FrameLayout rootFrame = new FrameLayout(context);
+ rootFrame.setPadding(40, 40, 40, 40);
+
+ // 主面板
+ LinearLayout mainPanel = new LinearLayout(context);
+ mainPanel.setOrientation(LinearLayout.VERTICAL);
+ mainPanel.setBackground(createRoundRect("#FFFFFF", 30f));
+ mainPanel.setPadding(50, 50, 50, 50);
+ mainPanel.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
+ ));
+
+ // 标题
+ TextView titleView = new TextView(context);
+ titleView.setText("消息设置");
+ titleView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 22);
+ titleView.setTextColor(Color.parseColor("#1A1A1A"));
+ titleView.getPaint().setFakeBoldText(true);
+ titleView.setPadding(0, 0, 0, 30);
+ mainPanel.addView(titleView);
+
+ // ===== 第一部分:欢迎语 =====
+ TextView welcomeTitle = new TextView(context);
+ welcomeTitle.setText("进群欢迎语");
+ welcomeTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
+ welcomeTitle.setTextColor(Color.parseColor("#333333"));
+ welcomeTitle.getPaint().setFakeBoldText(true);
+ welcomeTitle.setPadding(0, 0, 0, 10);
+ mainPanel.addView(welcomeTitle);
+
+ EditText welcomeInput = new EditText(context);
+ welcomeInput.setText(currentWelcome);
+ welcomeInput.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
+ welcomeInput.setHint("输入欢迎语...");
+ welcomeInput.setBackground(createRoundRect("#F5F5F5", 12f));
+ welcomeInput.setPadding(30, 25, 30, 25);
+ LinearLayout.LayoutParams wiParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
+ );
+ wiParams.setMargins(0, 0, 0, 15);
+ welcomeInput.setLayoutParams(wiParams);
+ mainPanel.addView(welcomeInput);
+
+ Button saveWelcomeBtn = new Button(context);
+ saveWelcomeBtn.setText("保存欢迎语");
+ saveWelcomeBtn.setTextColor(Color.WHITE);
+ saveWelcomeBtn.setBackground(createRoundRect(C_GREEN, 20f));
+ saveWelcomeBtn.setPadding(40, 15, 40, 15);
+ saveWelcomeBtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ String newWelcome = welcomeInput.getText().toString().trim();
+ if (newWelcome.isEmpty()) { toast("不能为空"); return; }
+ setString("gm_welcome", newWelcome);
+ toast("欢迎语已保存");
+ }
+ });
+ mainPanel.addView(saveWelcomeBtn);
+
+ // 分割线
+ View divider1 = new View(context);
+ divider1.setBackgroundColor(Color.parseColor("#F0F0F0"));
+ LinearLayout.LayoutParams d1Params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 2);
+ d1Params.setMargins(0, 30, 0, 30);
+ divider1.setLayoutParams(d1Params);
+ mainPanel.addView(divider1);
+
+ // ===== 第二部分:退群语 =====
+ TextView leaveTitle = new TextView(context);
+ leaveTitle.setText("退群通知语");
+ leaveTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
+ leaveTitle.setTextColor(Color.parseColor("#333333"));
+ leaveTitle.getPaint().setFakeBoldText(true);
+ leaveTitle.setPadding(0, 0, 0, 10);
+ mainPanel.addView(leaveTitle);
+
+ EditText leaveInput = new EditText(context);
+ leaveInput.setText(currentLeave);
+ leaveInput.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
+ leaveInput.setHint("输入退群通知语...");
+ leaveInput.setBackground(createRoundRect("#F5F5F5", 12f));
+ leaveInput.setPadding(30, 25, 30, 25);
+ LinearLayout.LayoutParams liParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT
+ );
+ liParams.setMargins(0, 0, 0, 15);
+ leaveInput.setLayoutParams(liParams);
+ mainPanel.addView(leaveInput);
+
+ Button saveLeaveBtn = new Button(context);
+ saveLeaveBtn.setText("保存退群语");
+ saveLeaveBtn.setTextColor(Color.WHITE);
+ saveLeaveBtn.setBackground(createRoundRect(C_RED, 20f));
+ saveLeaveBtn.setPadding(40, 15, 40, 15);
+ saveLeaveBtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ String newLeave = leaveInput.getText().toString().trim();
+ if (newLeave.isEmpty()) { toast("不能为空"); return; }
+ setString("gm_leave", newLeave);
+ toast("退群语已保存");
+ }
+ });
+ mainPanel.addView(saveLeaveBtn);
+
+ // 分割线
+ View divider2 = new View(context);
+ divider2.setBackgroundColor(Color.parseColor("#F0F0F0"));
+ LinearLayout.LayoutParams d2Params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 2);
+ d2Params.setMargins(0, 30, 0, 30);
+ divider2.setLayoutParams(d2Params);
+ mainPanel.addView(divider2);
+
+ // ===== 第三部分:返回按钮 =====
+ Button backBtn = new Button(context);
+ backBtn.setText("返回主面板");
+ backBtn.setTextColor(Color.WHITE);
+ backBtn.setBackground(createRoundRect(C_BLUE, 25f));
+ backBtn.setPadding(40, 15, 40, 15);
+ backBtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ dismissOld();
+ showMainPanel();
+ }
+ });
+ mainPanel.addView(backBtn);
+
+ rootFrame.addView(mainPanel);
+
+ _dlg = new AlertDialog.Builder(context).setView(rootFrame).create();
+ Window window = _dlg.getWindow();
+ if (window != null) {
+ window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ int sz = context.getResources().getDisplayMetrics().widthPixels;
+ window.setLayout(sz, sz);
+ }
+ _dlg.show();
+
+ // 入场动画
+ rootFrame.startAnimation(createEnterAnimation());
+
+ } catch (Exception e) {
+ log("[群监控] 设置面板异常: " + e.getMessage());
+ toast("界面加载失败");
+ }
+}