diff --git a/contacts/build.properties b/contacts/build.properties
index e3ca10c..363a895 100644
--- a/contacts/build.properties
+++ b/contacts/build.properties
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
-#Thu Apr 09 02:36:26 HKT 2026
+#Sat Apr 18 08:39:12 GMT 2026
stageCount=10
libraryProject=
baseVersion=15.14
publishVersion=15.14.9
-buildCount=0
+buildCount=25
baseBetaVersion=15.14.10
diff --git a/contacts/src/main/AndroidManifest.xml b/contacts/src/main/AndroidManifest.xml
index a201cb5..3a2a347 100644
--- a/contacts/src/main/AndroidManifest.xml
+++ b/contacts/src/main/AndroidManifest.xml
@@ -9,54 +9,73 @@
-
+
+
+
-
+
-
+
+
+
+
+
-
+
-
+
-
+
+
+
+
+
-
+
-
+
+
+
+
+
+
+
-
+
+
+
+
-
+
@@ -66,8 +85,11 @@
android:exported="true">
+
+
+
@@ -77,6 +99,7 @@
android:label="CallActivity"
android:launchMode="singleTask"
android:exported="true">
+
+
+
+
+
+
+
+
+
+
-
+ android:stopWithTask="false"/>
-
+ android:stopWithTask="false"/>
-
+ android:stopWithTask="false">
+
+
-
+
+
+
-
-
+ android:stopWithTask="false">
+
+
+
+
-
+
+
+
+
+
+ android:stopWithTask="false">
+
+
+
+
-
+
+
+
+
+
+
+
-
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/activities/UnitTestActivity.java b/contacts/src/main/java/cc/winboll/studio/contacts/activities/UnitTestActivity.java
index 7a60d28..8d3c2f5 100644
--- a/contacts/src/main/java/cc/winboll/studio/contacts/activities/UnitTestActivity.java
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/activities/UnitTestActivity.java
@@ -5,7 +5,9 @@ import android.view.View;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import cc.winboll.studio.contacts.R;
+import cc.winboll.studio.contacts.activities.UnitTestActivity;
import cc.winboll.studio.contacts.dun.Rules;
+import cc.winboll.studio.contacts.services.LimitedTimeSpecialChannelService;
import cc.winboll.studio.contacts.utils.IntUtils;
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
import cc.winboll.studio.libappbase.LogUtils;
@@ -116,6 +118,13 @@ public class UnitTestActivity extends WinBollActivity implements IWinBoLLActivit
LogUtils.d(TAG, String.format("onTestMain: 测试号码: %s | 匹配结果: %s", phone, isAllowed));
}
LogUtils.d(TAG, "onTestMain: 批量号码规则测试完成");
+
+ new Thread(new Runnable(){
+ @Override
+ public void run() {
+ LimitedTimeSpecialChannelService.unitTest(UnitTestActivity.this);
+ }
+ }).start();
}
// ====================== 私有工具函数区 ======================
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java b/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java
index 6a62579..0bc4402 100644
--- a/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/dun/Rules.java
@@ -5,6 +5,7 @@ import cc.winboll.studio.contacts.activities.SettingsActivity;
import cc.winboll.studio.contacts.bobulltoon.TomCat;
import cc.winboll.studio.contacts.model.PhoneConnectRuleBean;
import cc.winboll.studio.contacts.model.SettingsBean;
+import cc.winboll.studio.contacts.services.LimitedTimeSpecialChannelService;
import cc.winboll.studio.contacts.services.MainService;
import cc.winboll.studio.contacts.utils.ContactUtils;
import cc.winboll.studio.contacts.utils.IntUtils;
@@ -170,6 +171,14 @@ public class Rules {
isConnect = false;
LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
}
+
+ // 限时特殊通道打开时返回连接
+ if (!isDefend && LimitedTimeSpecialChannelService.isServiceRunning()) {
+ LogUtils.d(TAG, String.format("PhoneNumber %s\n and Limited Time Special Channel Service Is Running.", phoneNumber));
+ isDefend = true;
+ isConnect = true;
+ LogUtils.d(TAG, String.format("isDefend == %s\nisConnect == %s", isDefend, isConnect));
+ }
// 检验拨不通号码群
if (!isDefend && MainService.isPhoneInBoBullToon(phoneNumber)) {
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/services/LimitedTimeSpecialChannelService.java b/contacts/src/main/java/cc/winboll/studio/contacts/services/LimitedTimeSpecialChannelService.java
new file mode 100644
index 0000000..ae0ba19
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/services/LimitedTimeSpecialChannelService.java
@@ -0,0 +1,285 @@
+package cc.winboll.studio.contacts.services;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import cc.winboll.studio.libappbase.LogUtils;
+
+/**
+ * @Author 豆包&ZhanGSKen
+ * @Date 2026/04/18 15:00:00 (香港时区)
+ * @LastEditTime 2026/04/18 22:15:00 (香港时区)
+ * @Describe 限时特殊通道服务
+ * 提供安全的服务启动、令牌校验及循环任务管理
+ * 新增功能:
+ * 1. 计时过程中实时输出剩余秒数
+ * 2. 服务销毁前,发送本地广播通知应用内其他组件
+ */
+public class LimitedTimeSpecialChannelService extends Service {
+
+ // ========================= 常量定义 =========================
+ public static final String TAG = "LimitedTimeSpecialChannelService";
+ public static final String EXTRA_DELAY_MILLIS = "EXTRA_DELAY_MILLIS";
+ private static final String EXTRA_SECURITY_TOKEN = "EXTRA_SECURITY_TOKEN";
+
+ // 新增:本地广播 Action 常量
+ public static final String ACTION_SERVICE_DESTROYED = "cc.winboll.studio.contacts.services.ACTION_SERVICE_DESTROYED";
+
+ // ========================= 静态变量 =========================
+ /**
+ * 公共静态私有字段:安全校验令牌
+ * 确保全局可访问且值不可变更
+ */
+ public static final String mValidToken = "VALID_TOKEN_" + System.currentTimeMillis();
+
+ private static volatile LimitedTimeSpecialChannelService sInstance = null;
+ private static volatile boolean sIsServiceRunning = false;
+
+ // ========================= 成员变量 =========================
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private long mTotalMillis = 0; // 总定时时长
+ private long mRemainingMillis = 0; // 剩余时长
+ private LocalBroadcastManager mLocalBroadcastManager; // 本地广播管理器实例
+
+ // ========================= 公共静态方法 =========================
+ /**
+ * 公共静态方法:启动服务
+ * @param context 上下文
+ * @param delayMillis 定时时长(毫秒)
+ */
+ public static void startService(Context context, long delayMillis) {
+ LogUtils.i(TAG, "调用 startService 入口");
+ LogUtils.i(TAG, "入参 - context: " + context + ", delayMillis: " + delayMillis);
+
+ if (context == null) {
+ LogUtils.w(TAG, "启动失败,上下文为null");
+ return;
+ }
+ if (isServiceRunning()) {
+ LogUtils.i(TAG, "服务已运行,忽略重复启动");
+ return;
+ }
+
+ // 构建Intent传递参数
+ Intent intent = new Intent(context, LimitedTimeSpecialChannelService.class);
+ intent.putExtra(EXTRA_SECURITY_TOKEN, mValidToken);
+ intent.putExtra(EXTRA_DELAY_MILLIS, delayMillis);
+
+ context.startService(intent);
+ LogUtils.i(TAG, "服务启动命令已发出");
+ }
+
+ /**
+ * 公共静态方法:停止服务
+ * @param context 上下文
+ */
+ public static void stopService(Context context) {
+ LogUtils.i(TAG, "调用 stopService 入口");
+ LogUtils.i(TAG, "入参 - context: " + context);
+
+ if (context == null) {
+ LogUtils.w(TAG, "停止失败,上下文为null");
+ return;
+ }
+ Intent intent = new Intent(context, LimitedTimeSpecialChannelService.class);
+ context.stopService(intent);
+ LogUtils.i(TAG, "服务停止命令已发出");
+ }
+
+ /**
+ * 公共静态方法:查询服务运行状态
+ * @return true if running
+ */
+ public static boolean isServiceRunning() {
+ return sIsServiceRunning;
+ }
+
+ /**
+ * 【核心单元测试方法】
+ * 执行完整的单元测试流程
+ * @param context 上下文
+ */
+ public static void unitTest(Context context) {
+ LogUtils.i(TAG, "=== 开始执行单元测试 ===");
+
+ // 测试1: 初始状态应为未运行
+ boolean initialState = isServiceRunning();
+ LogUtils.i(TAG, "测试1 - 初始状态检查: " + (initialState ? "失败(不应运行)" : "成功(未运行)"));
+
+ // 启动服务,设置5秒定时
+ startService(context, 5000L);
+
+ // 核心修复:同步等待服务启动完成
+ try {
+ Thread.sleep(1200);
+ } catch (InterruptedException e) {
+ LogUtils.e(TAG, "单元测试等待被中断", e);
+ }
+
+ // 测试3: 验证服务已启动
+ boolean runningState = isServiceRunning();
+ LogUtils.i(TAG, "测试3 - 运行状态检查: " + (runningState ? "成功(运行中)" : "失败(未运行)"));
+
+ // 测试4: 尝试重复启动
+ LogUtils.i(TAG, "测试4 - 尝试重复启动(预期忽略)");
+ startService(context, 5000L);
+
+ // 测试5: 等待服务自动停止
+ LogUtils.i(TAG, "测试5 - 等待服务自动停止(5秒)...");
+ try {
+ // 等待超过服务的5秒运行时间,确保能观测到销毁状态
+ Thread.sleep(6000);
+ } catch (InterruptedException e) {
+ LogUtils.e(TAG, "测试等待被中断", e);
+ }
+
+ // 验证最终状态
+ boolean finalState = isServiceRunning();
+ LogUtils.i(TAG, "测试5 - 最终状态检查: " + (!finalState ? "成功(已销毁)" : "失败(仍运行)"));
+ LogUtils.i(TAG, "=== 单元测试执行完成 ===");
+ }
+
+ // ========================= 生命周期 =========================
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ LogUtils.i(TAG, "服务 onCreate,创建实例");
+ sInstance = this;
+ sIsServiceRunning = true;
+
+ // 初始化本地广播管理器
+ mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ LogUtils.i(TAG, "服务 onStartCommand,处理启动请求");
+ LogUtils.i(TAG, "入参 - intent: " + intent + ", flags: " + flags + ", startId: " + startId);
+
+ if (!isValidToken(intent)) {
+ LogUtils.w(TAG, "安全校验失败,拒绝启动");
+ stopSelf();
+ return START_NOT_STICKY;
+ }
+
+ long delayMillis = intent.getLongExtra(EXTRA_DELAY_MILLIS, 0);
+ if (delayMillis <= 0) {
+ LogUtils.w(TAG, "无效的定时时长: " + delayMillis + ",服务将退出");
+ stopSelf();
+ return START_NOT_STICKY;
+ }
+
+ // 初始化总时长和剩余时长
+ mTotalMillis = delayMillis;
+ mRemainingMillis = delayMillis;
+ LogUtils.i(TAG, "初始化定时时长: " + (mTotalMillis / 1000) + " 秒");
+
+ startLoopTask();
+ // 设置自动停止定时器
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ LogUtils.i(TAG, "定时结束,准备停止服务");
+ stopSelf();
+ }
+ }, delayMillis);
+
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ LogUtils.i(TAG, "服务 onDestroy,准备销毁");
+
+ // 新增:发送本地广播,通知应用内其他组件服务即将销毁
+ sendServiceDestroyedBroadcast();
+
+ // 原有逻辑:重置状态
+ sIsServiceRunning = false;
+ sInstance = null;
+
+ // 原有逻辑:清理所有回调
+ mHandler.removeCallbacksAndMessages(null);
+
+ // 原有逻辑:执行父类销毁
+ super.onDestroy();
+
+ LogUtils.i(TAG, "服务销毁完成");
+ }
+
+ /**
+ * 新增:发送服务销毁的本地广播
+ * 确保应用内所有注册了该广播接收器的组件都能收到通知
+ */
+ private void sendServiceDestroyedBroadcast() {
+ try {
+ Intent intent = new Intent(ACTION_SERVICE_DESTROYED);
+ // 可在此处添加额外数据,例如剩余时长等
+ intent.putExtra("REMAINING_SECONDS", mRemainingMillis / 1000);
+ // 使用本地广播管理器发送
+ LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
+ LogUtils.i(TAG, "已发送本地广播 - 服务销毁通知: " + ACTION_SERVICE_DESTROYED);
+ } catch (Exception e) {
+ LogUtils.e(TAG, "发送服务销毁广播失败", e);
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ LogUtils.i(TAG, "服务 onBind,不支持绑定");
+ // 不支持绑定
+ return null;
+ }
+
+ // ========================= 私有辅助方法 =========================
+ /**
+ * 校验令牌有效性
+ */
+ private boolean isValidToken(Intent intent) {
+ LogUtils.i(TAG, "调用 isValidToken 校验");
+ LogUtils.i(TAG, "入参 - intent: " + intent);
+
+ if (intent == null) {
+ return false;
+ }
+ String incomingToken = intent.getStringExtra(EXTRA_SECURITY_TOKEN);
+ LogUtils.i(TAG, "接收到外部传入令牌: " + incomingToken);
+ LogUtils.i(TAG, "比对本地存储令牌: " + mValidToken);
+ return mValidToken.equals(incomingToken);
+ }
+
+ /**
+ * 启动循环任务
+ */
+ private void startLoopTask() {
+ mHandler.removeCallbacks(mLoopTaskRunnable);
+ mHandler.postDelayed(mLoopTaskRunnable, 1000);
+ }
+
+ /**
+ * 循环任务心跳
+ * 新增:实时计算并输出剩余秒数
+ */
+ private final Runnable mLoopTaskRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (sIsServiceRunning) {
+ // 计算剩余秒数
+ long remainingSeconds = mRemainingMillis / 1000;
+ // 输出剩余秒数信息
+ LogUtils.i(TAG, "循环任务心跳:服务运行中,剩余 " + remainingSeconds + " 秒");
+
+ // 剩余时长递减
+ mRemainingMillis -= 1000;
+
+ // 递归调用,形成循环
+ startLoopTask();
+ }
+ }
+ };
+}
+
diff --git a/contacts/src/main/java/cc/winboll/studio/contacts/views/LimitedTimeSpecialChannelView.java b/contacts/src/main/java/cc/winboll/studio/contacts/views/LimitedTimeSpecialChannelView.java
new file mode 100644
index 0000000..918068e
--- /dev/null
+++ b/contacts/src/main/java/cc/winboll/studio/contacts/views/LimitedTimeSpecialChannelView.java
@@ -0,0 +1,252 @@
+package cc.winboll.studio.contacts.views;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import cc.winboll.studio.contacts.R;
+import cc.winboll.studio.contacts.services.LimitedTimeSpecialChannelService;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.libappbase.ToastUtils;
+
+/**
+ * @Author ZhanGSKen
+ * @Date 2026/04/18 15:05
+ * @LastEditTime 2026/04/18 22:30:00 (香港时区)
+ * @Describe 限时特殊通道视图
+ * 功能更新:
+ * 1. Switch 打开时,EditText 不可编辑,布局背景变为绿色
+ * 2. Switch 关闭时,EditText 可编辑,布局背景恢复为白色
+ * 3. 监听服务销毁本地广播,到达后自动关闭 Switch 并恢复背景色
+ */
+public class LimitedTimeSpecialChannelView extends LinearLayout {
+
+ // ====================== 常量定义 =========================
+ public static final String TAG = "LimitedTimeSpecialChannelView";
+
+ // ====================== 成员变量 =========================
+ private Context mContext;
+ private EditText mEtSeconds;
+ private Switch mSwEnable;
+ private LinearLayout mLlMain;
+
+ // ====================== 构造函数 =========================
+ /**
+ * 代码创建View时调用
+ * @param context 上下文
+ */
+ public LimitedTimeSpecialChannelView(Context context) {
+ super(context);
+ initView(context);
+ }
+
+ /**
+ * XML布局中引用时调用
+ * @param context 上下文
+ * @param attrs 属性集合
+ */
+ public LimitedTimeSpecialChannelView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initView(context);
+ }
+
+ /**
+ * 指定样式属性
+ * @param context 上下文
+ * @param attrs 属性集合
+ * @param defStyleAttr 默认样式属性
+ */
+ public LimitedTimeSpecialChannelView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initView(context);
+ }
+
+ /**
+ * 指定样式资源
+ * @param context 上下文
+ * @param attrs 属性集合
+ * @param defStyleAttr 默认样式属性
+ * @param defStyleRes 默认样式资源
+ */
+ public LimitedTimeSpecialChannelView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ initView(context);
+ }
+
+ // ====================== 初始化方法 ========================
+ /**
+ * 初始化视图
+ * 加载布局文件,绑定控件,并注册广播接收器
+ * @param context 上下文
+ */
+ private void initView(Context context) {
+ LogUtils.i(TAG, "初始化限时特殊通道视图");
+ this.mContext = context;
+
+ // 1. 加载布局文件
+ LayoutInflater.from(context)
+ .inflate(R.layout.view_limitedtimespecialchannel, this, true);
+
+ LogUtils.i(TAG, "布局加载完成:view_limitedtimespecialchannel.xml");
+
+ // 2. 绑定核心控件引用
+ bindViews();
+
+ // 3. 初始化开关状态与背景色关联
+ initSwitchState();
+
+ // 4. 注册服务销毁本地广播监听
+ registerServiceDestroyedListener();
+ }
+
+ /**
+ * 绑定布局中的控件ID
+ */
+ private void bindViews() {
+ mEtSeconds = (EditText) findViewById(R.id.et_seconds);
+ mSwEnable = (Switch) findViewById(R.id.sw_enable);
+ mLlMain = (LinearLayout) findViewById(R.id.ll_main);
+
+ // 健壮性检查
+ if (mEtSeconds == null) {
+ LogUtils.e(TAG, "未找到ID为 et_seconds 的EditText");
+ }
+ if (mSwEnable == null) {
+ LogUtils.e(TAG, "未找到ID为 sw_enable 的Switch");
+ }
+ if (mLlMain == null) {
+ LogUtils.w(TAG, "未找到ID为 ll_main 的LinearLayout,将无法设置背景色");
+ }
+ }
+
+ /**
+ * 初始化开关状态
+ * 设置Switch的监听事件,以及初始背景色
+ */
+ private void initSwitchState() {
+ // 设置初始状态
+ boolean isChecked = mSwEnable.isChecked();
+ mEtSeconds.setEnabled(!isChecked);
+ if (mLlMain != null) {
+ mLlMain.setBackgroundColor(isChecked ? android.graphics.Color.GREEN : android.graphics.Color.WHITE);
+ }
+ LogUtils.i(TAG, "初始状态 - 开关: " + (isChecked ? "启用" : "关闭") + ", 背景色: " + (isChecked ? "绿色" : "白色"));
+
+ // 为Switch设置切换监听
+ mSwEnable.setOnCheckedChangeListener(new Switch.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ LogUtils.i(TAG, "开关状态变更: " + (isChecked ? "启用" : "关闭"));
+
+ // 核心逻辑:控制EditText的可编辑状态
+ mEtSeconds.setEnabled(!isChecked);
+
+ // 控制布局背景色
+ if (mLlMain != null) {
+ mLlMain.setBackgroundColor(isChecked ? android.graphics.Color.GREEN : android.graphics.Color.WHITE);
+ }
+
+ // 控制服务启动与停止
+ if (isChecked) {
+ // 打开开关时,启动服务
+ startLimitedTimeSpecialChannelService();
+ } else {
+ // 关闭开关时,停止服务
+ LimitedTimeSpecialChannelService.stopService(mContext);
+ }
+ }
+ });
+ }
+
+ /**
+ * 注册服务销毁本地广播监听
+ * 当服务销毁消息到达时,自动关闭开关并恢复背景色
+ */
+ private void registerServiceDestroyedListener() {
+ // 创建广播接收器
+ LocalBroadcastManager.getInstance(mContext)
+ .registerReceiver(new android.content.BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // 校验广播Action
+ if (LimitedTimeSpecialChannelService.ACTION_SERVICE_DESTROYED.equals(intent.getAction())) {
+ LogUtils.i(TAG, "收到服务销毁本地广播,准备关闭开关并恢复背景");
+
+ // 核心逻辑:关闭Switch
+ if (mSwEnable != null && mSwEnable.isChecked()) {
+ mSwEnable.setChecked(false);
+ }
+
+ // 恢复布局背景色为白色
+ if (mLlMain != null) {
+ mLlMain.setBackgroundColor(android.graphics.Color.WHITE);
+ }
+
+ // 可选:通知用户服务已销毁
+ ToastUtils.show("服务已销毁,恢复初始状态");
+ }
+ }
+ }, new IntentFilter(LimitedTimeSpecialChannelService.ACTION_SERVICE_DESTROYED));
+ }
+
+ /**
+ * 启动限时特殊通道服务
+ * 读取输入框中的秒数,转换为毫秒后启动服务
+ */
+ private void startLimitedTimeSpecialChannelService() {
+ LogUtils.i(TAG, "检测到秒数输入框点击,准备启动服务");
+
+ // 1. 获取输入的秒数
+ String secondsStr = mEtSeconds.getText().toString().trim();
+ if (secondsStr.isEmpty()) {
+ LogUtils.w(TAG, "输入框为空,使用默认值 3600 秒");
+ secondsStr = "3600";
+ }
+
+ try {
+ // 2. 转换为毫秒数 (1秒 = 1000毫秒)
+ long seconds = Long.parseLong(secondsStr);
+ long delayMillis = seconds * 1000;
+
+ LogUtils.i(TAG, "输入秒数: " + seconds + " 秒,转换为毫秒: " + delayMillis);
+
+ // 3. 调用服务启动方法
+ ToastUtils.show("调用 LimitedTimeSpecialChannelService 服务");
+ LimitedTimeSpecialChannelService.startService(mContext, delayMillis);
+
+ } catch (NumberFormatException e) {
+ LogUtils.e(TAG, "输入的秒数格式错误: " + secondsStr, e);
+ }
+ }
+
+ /**
+ *【可选】视图销毁时反注册广播,防止内存泄漏
+ */
+// @Override
+// protected void onDetachedFromWindow() {
+// super.onDetachedFromWindow();
+// // 反注册本地广播
+// LocalBroadcastManager.getInstance(mContext).unregisterregisterReceiver(mServiceDestroyedReceiver);
+// LogUtils.i(TAG, "反注册服务销毁本地广播完成");
+// }
+
+// //【可选】定义广播接收器成员变量,方便反注册
+// private android.content.BroadcastReceiver mServiceDestroyedReceiver;
+//
+// private void registerServiceDestroyedListener() {
+// mServiceDestroyedReceiver = new android.content.BroadcastReceiver() {
+// @Override
+// public void onReceive(Context context, Intent intent) {
+// // ... 原有onReceive逻辑 ...
+// }
+// };
+// LocalBroadcastManager.getInstance(mContext).registerReceiver(mServiceDestroyedReceiver, new IntentFilter(LimitedTimeSpecialChannelService.ACTION_SERVICE_DESTROYED));
+// }
+}
+
diff --git a/contacts/src/main/res/layout/activity_settings.xml b/contacts/src/main/res/layout/activity_settings.xml
index 6ab5717..08335f9 100644
--- a/contacts/src/main/res/layout/activity_settings.xml
+++ b/contacts/src/main/res/layout/activity_settings.xml
@@ -57,6 +57,24 @@
android:id="@+id/tv_DunInfo"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+