diff --git a/contacts/build.properties b/contacts/build.properties index 4493b46..8c54dfe 100644 --- a/contacts/build.properties +++ b/contacts/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Sat Apr 18 16:47:18 HKT 2026 +#Sat Apr 18 12:52:16 GMT 2026 stageCount=11 libraryProject= baseVersion=15.14 publishVersion=15.14.10 -buildCount=0 +buildCount=18 baseBetaVersion=15.14.11 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 index ae0ba19..aa62f02 100644 --- a/contacts/src/main/java/cc/winboll/studio/contacts/services/LimitedTimeSpecialChannelService.java +++ b/contacts/src/main/java/cc/winboll/studio/contacts/services/LimitedTimeSpecialChannelService.java @@ -11,13 +11,14 @@ import cc.winboll.studio.libappbase.LogUtils; /** * @Author 豆包&ZhanGSKen - * @Date 2026/04/18 15:00:00 (香港时区) - * @LastEditTime 2026/04/18 22:15:00 (香港时区) + * @Date 2026/04/18 15:00:00 (GMT+8) + * @LastEditTime 2026/04/22 17:20:00 (GMT+8) * @Describe 限时特殊通道服务 * 提供安全的服务启动、令牌校验及循环任务管理 * 新增功能: * 1. 计时过程中实时输出剩余秒数 - * 2. 服务销毁前,发送本地广播通知应用内其他组件 + * 2. 服务销毁前,发送本地广播通知应用内其他组件,并携带剩余秒数信息 + * 3. 每秒发送一次倒计时状态的本地广播 */ public class LimitedTimeSpecialChannelService extends Service { @@ -26,9 +27,16 @@ public class LimitedTimeSpecialChannelService extends Service { public static final String EXTRA_DELAY_MILLIS = "EXTRA_DELAY_MILLIS"; private static final String EXTRA_SECURITY_TOKEN = "EXTRA_SECURITY_TOKEN"; - // 新增:本地广播 Action 常量 + // 本地广播 Action 常量 public static final String ACTION_SERVICE_DESTROYED = "cc.winboll.studio.contacts.services.ACTION_SERVICE_DESTROYED"; + // 倒计时心跳广播 Action + public static final String ACTION_COUNTDOWN_TICK = "cc.winboll.studio.contacts.services.ACTION_COUNTDOWN_TICK"; + // 广播携带的额外参数:剩余秒数 + public static final String EXTRA_REMAINING_SECONDS = "EXTRA_REMAINING_SECONDS"; + // 广播携带的额外参数:总定时秒数 + public static final String EXTRA_TOTAL_SECONDS = "EXTRA_TOTAL_SECONDS"; + // ========================= 静态变量 ========================= /** * 公共静态私有字段:安全校验令牌 @@ -52,7 +60,7 @@ public class LimitedTimeSpecialChannelService extends Service { * @param delayMillis 定时时长(毫秒) */ public static void startService(Context context, long delayMillis) { - LogUtils.i(TAG, "调用 startService 入口"); + LogUtils.i(TAG, "调用静态入口方法 startService"); LogUtils.i(TAG, "入参 - context: " + context + ", delayMillis: " + delayMillis); if (context == null) { @@ -60,7 +68,7 @@ public class LimitedTimeSpecialChannelService extends Service { return; } if (isServiceRunning()) { - LogUtils.i(TAG, "服务已运行,忽略重复启动"); + LogUtils.i(TAG, "服务已运行,忽略重复启动请求"); return; } @@ -78,7 +86,7 @@ public class LimitedTimeSpecialChannelService extends Service { * @param context 上下文 */ public static void stopService(Context context) { - LogUtils.i(TAG, "调用 stopService 入口"); + LogUtils.i(TAG, "调用静态入口方法 stopService"); LogUtils.i(TAG, "入参 - context: " + context); if (context == null) { @@ -153,6 +161,7 @@ public class LimitedTimeSpecialChannelService extends Service { // 初始化本地广播管理器 mLocalBroadcastManager = LocalBroadcastManager.getInstance(this); + LogUtils.i(TAG, "本地广播管理器初始化完成"); } @Override @@ -161,7 +170,7 @@ public class LimitedTimeSpecialChannelService extends Service { LogUtils.i(TAG, "入参 - intent: " + intent + ", flags: " + flags + ", startId: " + startId); if (!isValidToken(intent)) { - LogUtils.w(TAG, "安全校验失败,拒绝启动"); + LogUtils.w(TAG, "安全校验失败,拒绝启动服务"); stopSelf(); return START_NOT_STICKY; } @@ -183,7 +192,7 @@ public class LimitedTimeSpecialChannelService extends Service { mHandler.postDelayed(new Runnable() { @Override public void run() { - LogUtils.i(TAG, "定时结束,准备停止服务"); + LogUtils.i(TAG, "定时时长结束,准备停止服务"); stopSelf(); } }, delayMillis); @@ -195,42 +204,63 @@ public class LimitedTimeSpecialChannelService extends Service { public void onDestroy() { LogUtils.i(TAG, "服务 onDestroy,准备销毁"); - // 新增:发送本地广播,通知应用内其他组件服务即将销毁 + // 发送本地广播,通知应用内其他组件服务即将销毁 + // 此处携带剩余秒数信息,方便接收端知晓具体的结束状态 sendServiceDestroyedBroadcast(); - // 原有逻辑:重置状态 + // 重置运行状态 sIsServiceRunning = false; sInstance = null; - // 原有逻辑:清理所有回调 + // 清理所有回调 mHandler.removeCallbacksAndMessages(null); - // 原有逻辑:执行父类销毁 + // 执行父类销毁 super.onDestroy(); - LogUtils.i(TAG, "服务销毁完成"); + LogUtils.i(TAG, "服务销毁流程完成"); } /** - * 新增:发送服务销毁的本地广播 + * 发送服务销毁的本地广播 * 确保应用内所有注册了该广播接收器的组件都能收到通知 + * [新增] 携带当前剩余秒数信息 */ private void sendServiceDestroyedBroadcast() { + LogUtils.i(TAG, "准备发送服务销毁本地广播"); 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); + // 新增:将当前剩余秒数放入广播附加数据中 + intent.putExtra(EXTRA_REMAINING_SECONDS, mRemainingMillis / 1000); + + mLocalBroadcastManager.sendBroadcast(intent); + LogUtils.i(TAG, "服务销毁广播发送成功,Action: " + ACTION_SERVICE_DESTROYED); + LogUtils.i(TAG, "广播附加数据 - 剩余秒数: " + (mRemainingMillis / 1000)); } catch (Exception e) { LogUtils.e(TAG, "发送服务销毁广播失败", e); } } + /** + * 发送倒计时心跳的本地广播 + * 每秒执行一次,携带当前剩余秒数和总时长 + */ + private void sendCountdownTickBroadcast() { + try { + Intent intent = new Intent(ACTION_COUNTDOWN_TICK); + intent.putExtra(EXTRA_REMAINING_SECONDS, mRemainingMillis / 1000); + intent.putExtra(EXTRA_TOTAL_SECONDS, mTotalMillis / 1000); + + mLocalBroadcastManager.sendBroadcast(intent); + // 日志已精简,只在关键节点打印 + } catch (Exception e) { + LogUtils.e(TAG, "发送倒计时广播失败", e); + } + } + @Override public IBinder onBind(Intent intent) { - LogUtils.i(TAG, "服务 onBind,不支持绑定"); + LogUtils.i(TAG, "服务 onBind,不支持绑定操作"); // 不支持绑定 return null; } @@ -240,7 +270,7 @@ public class LimitedTimeSpecialChannelService extends Service { * 校验令牌有效性 */ private boolean isValidToken(Intent intent) { - LogUtils.i(TAG, "调用 isValidToken 校验"); + LogUtils.i(TAG, "调用 isValidToken 方法进行安全校验"); LogUtils.i(TAG, "入参 - intent: " + intent); if (intent == null) { @@ -248,7 +278,7 @@ public class LimitedTimeSpecialChannelService extends Service { } String incomingToken = intent.getStringExtra(EXTRA_SECURITY_TOKEN); LogUtils.i(TAG, "接收到外部传入令牌: " + incomingToken); - LogUtils.i(TAG, "比对本地存储令牌: " + mValidToken); + LogUtils.i(TAG, "本地校验令牌: " + mValidToken); return mValidToken.equals(incomingToken); } @@ -256,30 +286,42 @@ public class LimitedTimeSpecialChannelService extends Service { * 启动循环任务 */ private void startLoopTask() { + LogUtils.i(TAG, "启动倒计时循环任务"); mHandler.removeCallbacks(mLoopTaskRunnable); mHandler.postDelayed(mLoopTaskRunnable, 1000); } /** * 循环任务心跳 - * 新增:实时计算并输出剩余秒数 + * 核心逻辑:先递减时长,再发送广播,确保0秒能被正确发送 */ private final Runnable mLoopTaskRunnable = new Runnable() { @Override public void run() { if (sIsServiceRunning) { - // 计算剩余秒数 - long remainingSeconds = mRemainingMillis / 1000; - // 输出剩余秒数信息 - LogUtils.i(TAG, "循环任务心跳:服务运行中,剩余 " + remainingSeconds + " 秒"); + // 核心修复:先递减剩余时长 + if (mRemainingMillis >= 1000) { + mRemainingMillis -= 1000; + } else { + // 兜底:防止负数,直接置为0 + mRemainingMillis = 0; + } - // 剩余时长递减 - mRemainingMillis -= 1000; + // 计算并发送当前剩余秒数 + long remainingSeconds = mRemainingMillis / 1000; + // 日志已精简,只在非0状态打印,避免日志刷屏 + if (remainingSeconds > 0) { + LogUtils.i(TAG, "循环心跳:剩余 " + remainingSeconds + " 秒"); + } + + // 发送倒计时广播 + sendCountdownTickBroadcast(); // 递归调用,形成循环 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 index 918068e..b2687df 100644 --- a/contacts/src/main/java/cc/winboll/studio/contacts/views/LimitedTimeSpecialChannelView.java +++ b/contacts/src/main/java/cc/winboll/studio/contacts/views/LimitedTimeSpecialChannelView.java @@ -5,6 +5,8 @@ import android.content.Intent; import android.content.IntentFilter; import android.util.AttributeSet; import android.view.LayoutInflater; +import android.view.View; +import android.widget.Button; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.LinearLayout; @@ -16,193 +18,273 @@ 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 (香港时区) + * @Author 豆包&ZhanGSKen + * @Date 2026/04/18 15:05:00 (GMT+8) + * @LastEditTime 2026/04/22 17:30:00 (GMT+8) * @Describe 限时特殊通道视图 - * 功能更新: - * 1. Switch 打开时,EditText 不可编辑,布局背景变为绿色 - * 2. Switch 关闭时,EditText 可编辑,布局背景恢复为白色 - * 3. 监听服务销毁本地广播,到达后自动关闭 Switch 并恢复背景色 + * 功能说明: + * 1. Switch 打开时,EditText 不可编辑,布局背景变为绿色,加减按钮不可用 + * 2. Switch 关闭时,EditText 可编辑,布局背景恢复为白色,加减按钮恢复可用 + * 3. 监听服务销毁本地广播,到达后自动关闭 Switch 并恢复背景色与按钮状态 + * 4. 监听倒计时心跳广播,实时更新EditText显示剩余秒数 + * 5. 加减按钮控制秒数增减,最小值限制为0 + * 6. 新增快速设置按钮(btn_thisSeconds),点击直接设置为当前量级3600秒 + * 7. [新增] 服务销毁时,恢复EditText显示为倒计时剩余秒数 */ public class LimitedTimeSpecialChannelView extends LinearLayout { // ====================== 常量定义 ========================= public static final String TAG = "LimitedTimeSpecialChannelView"; + // 定义秒数增量的量级 + private static final long SECONDS_INCREMENT = 3600; // ====================== 成员变量 ========================= private Context mContext; private EditText mEtSeconds; private Switch mSwEnable; private LinearLayout mLlMain; + private Button mBtnAddSeconds; + private Button mBtnSubSeconds; + // [新增] 快速设置量级按钮 + private Button mBtnThisSeconds; + private android.content.BroadcastReceiver mCountdownReceiver; // ====================== 构造函数 ========================= - /** - * 代码创建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, "初始化限时特殊通道视图"); + LogUtils.i(TAG, "开始初始化视图..."); this.mContext = context; - // 1. 加载布局文件 - LayoutInflater.from(context) - .inflate(R.layout.view_limitedtimespecialchannel, this, true); + // 加载布局文件 + LayoutInflater.from(context).inflate(R.layout.view_limitedtimespecialchannel, this, true); + LogUtils.i(TAG, "布局文件加载完成"); - LogUtils.i(TAG, "布局加载完成:view_limitedtimespecialchannel.xml"); - - // 2. 绑定核心控件引用 + // 绑定控件ID bindViews(); - - // 3. 初始化开关状态与背景色关联 + // 初始化所有按钮点击事件 + initButtons(); + // 初始化开关状态 initSwitchState(); - - // 4. 注册服务销毁本地广播监听 + // 注册广播监听 registerServiceDestroyedListener(); + registerCountdownTickListener(); + + LogUtils.i(TAG, "视图初始化流程结束"); } /** - * 绑定布局中的控件ID + * 绑定布局中的所有控件ID */ private void bindViews() { + LogUtils.i(TAG, "开始绑定控件ID"); mEtSeconds = (EditText) findViewById(R.id.et_seconds); mSwEnable = (Switch) findViewById(R.id.sw_enable); mLlMain = (LinearLayout) findViewById(R.id.ll_main); + mBtnAddSeconds = (Button) findViewById(R.id.btn_addSeconds); + mBtnSubSeconds = (Button) findViewById(R.id.btn_subSeconds); + // [新增] 绑定快速设置按钮ID + mBtnThisSeconds = (Button) findViewById(R.id.btn_thisSeconds); // 健壮性检查 - if (mEtSeconds == null) { - LogUtils.e(TAG, "未找到ID为 et_seconds 的EditText"); + 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,将无法设置背景色"); + if (mBtnAddSeconds == null) LogUtils.e(TAG, "未找到ID为 btn_addSeconds 的Button"); + if (mBtnSubSeconds == null) LogUtils.e(TAG, "未找到ID为 btn_subSeconds 的Button"); + if (mBtnThisSeconds == null) LogUtils.e(TAG, "未找到ID为 btn_thisSeconds 的Button"); + + LogUtils.i(TAG, "控件ID绑定完成"); + } + + /** + * 初始化所有按钮的点击事件 + * 包含:加号、减号、快速设置量级按钮 + */ + private void initButtons() { + LogUtils.i(TAG, "初始化按钮点击事件"); + + // 1. 加号按钮:增加一个量级 + if (mBtnAddSeconds != null) { + mBtnAddSeconds.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + LogUtils.i(TAG, "点击加号按钮,准备增加秒数"); + try { + long current = Long.parseLong(mEtSeconds.getText().toString().trim()); + long newValue = current + SECONDS_INCREMENT; + mEtSeconds.setText(String.valueOf(newValue)); + LogUtils.i(TAG, "增加成功,当前秒数: " + newValue); + } catch (NumberFormatException e) { + LogUtils.e(TAG, "当前输入格式错误,无法增加", e); + mEtSeconds.setText("0"); + } + } + }); } - if (mSwEnable == null) { - LogUtils.e(TAG, "未找到ID为 sw_enable 的Switch"); + + // 2. 减号按钮:减少一个量级,最低为0 + if (mBtnSubSeconds != null) { + mBtnSubSeconds.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + LogUtils.i(TAG, "点击减号按钮,准备减少秒数"); + try { + long current = Long.parseLong(mEtSeconds.getText().toString().trim()); + long newValue = Math.max(0, current - SECONDS_INCREMENT); + mEtSeconds.setText(String.valueOf(newValue)); + LogUtils.i(TAG, "减少成功,当前秒数: " + newValue); + } catch (NumberFormatException e) { + LogUtils.e(TAG, "当前输入格式错误,无法减少", e); + mEtSeconds.setText("0"); + } + } + }); } - if (mLlMain == null) { - LogUtils.w(TAG, "未找到ID为 ll_main 的LinearLayout,将无法设置背景色"); + + // 3. [新增] 快速设置按钮:点击直接设置为量级数值 3600 + if (mBtnThisSeconds != null) { + mBtnThisSeconds.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + LogUtils.i(TAG, "点击快速设置按钮,设置秒数为: " + SECONDS_INCREMENT); + // 直接设置为常量数值 + mEtSeconds.setText(String.valueOf(SECONDS_INCREMENT)); + LogUtils.i(TAG, "快速设置成功,当前秒数: " + SECONDS_INCREMENT); + } + }); } } /** * 初始化开关状态 - * 设置Switch的监听事件,以及初始背景色 + * 设置Switch的监听事件,以及背景色、按钮可点击状态 */ private void initSwitchState() { - // 设置初始状态 + LogUtils.i(TAG, "初始化开关状态"); 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设置切换监听 + // 设置初始UI状态 + updateUIState(!isChecked); + + LogUtils.i(TAG, "初始状态 - 开关: " + (isChecked ? "开启" : "关闭") + ", 背景色: " + (isChecked ? "绿色" : "白色") + ", 按钮可用: " + (!isChecked ? "是" : "否")); + + // 设置开关切换监听 mSwEnable.setOnCheckedChangeListener(new Switch.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - LogUtils.i(TAG, "开关状态变更: " + (isChecked ? "启用" : "关闭")); + LogUtils.i(TAG, "开关状态变更为: " + (isChecked ? "开启" : "关闭")); + // 根据开关状态更新UI可操作状态 + updateUIState(!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); } } }); } + /** + * 统一更新UI可操作状态 + * @param enabled 可用状态 + */ + private void updateUIState(boolean enabled) { + if (mEtSeconds != null) mEtSeconds.setEnabled(enabled); + if (mBtnAddSeconds != null) mBtnAddSeconds.setEnabled(enabled); + if (mBtnSubSeconds != null) mBtnSubSeconds.setEnabled(enabled); + // [新增] 同步快速设置按钮状态 + if (mBtnThisSeconds != null) mBtnThisSeconds.setEnabled(enabled); + + if (mLlMain != null) { + mLlMain.setBackgroundColor(enabled ? android.graphics.Color.WHITE : android.graphics.Color.GREEN); + } + } + /** * 注册服务销毁本地广播监听 - * 当服务销毁消息到达时,自动关闭开关并恢复背景色 + * [修改] 接收广播时检索倒计时剩余秒数参数并设置到EditText */ private void registerServiceDestroyedListener() { - // 创建广播接收器 - LocalBroadcastManager.getInstance(mContext) - .registerReceiver(new android.content.BroadcastReceiver() { + LogUtils.i(TAG, "注册服务销毁广播监听"); + IntentFilter filter = new IntentFilter(LimitedTimeSpecialChannelService.ACTION_SERVICE_DESTROYED); + 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, "收到服务销毁本地广播,准备关闭开关并恢复背景"); + LogUtils.i(TAG, "收到服务销毁广播,执行恢复操作"); - // 核心逻辑:关闭Switch + // 1. 恢复开关状态 if (mSwEnable != null && mSwEnable.isChecked()) { mSwEnable.setChecked(false); } - // 恢复布局背景色为白色 - if (mLlMain != null) { - mLlMain.setBackgroundColor(android.graphics.Color.WHITE); + // 2. [核心修改] 检索并设置倒计时剩余秒数参数 + // 从广播附加数据中获取之前服务发送的剩余秒数 + long remainingSeconds = intent.getLongExtra(LimitedTimeSpecialChannelService.EXTRA_REMAINING_SECONDS, 0); + // 实际倒计时消耗冲正 + remainingSeconds -= 1; + // 倒计时秒数0值保底 + remainingSeconds = remainingSeconds < 0? 0 : remainingSeconds; + LogUtils.i(TAG, "从销毁广播中检索到剩余秒数参数: " + remainingSeconds + " 秒"); + //ToastUtils.show("从销毁广播中检索到剩余秒数参数: " + remainingSeconds + " 秒"); + + // 3. 恢复所有UI状态为可用 + updateUIState(true); + // 4. 设置EditText显示为倒计时剩余秒数 + if (mEtSeconds != null) { + mEtSeconds.setText(String.valueOf(remainingSeconds)); + LogUtils.i(TAG, "已将EditText设置为剩余秒数: " + remainingSeconds); } - // 可选:通知用户服务已销毁 - ToastUtils.show("服务已销毁,恢复初始状态"); + ToastUtils.show("服务已销毁,恢复倒计时剩余状态"); } } - }, new IntentFilter(LimitedTimeSpecialChannelService.ACTION_SERVICE_DESTROYED)); + }, filter); + } + + /** + * 注册倒计时心跳广播监听 + */ + private void registerCountdownTickListener() { + LogUtils.i(TAG, "注册倒计时心跳广播监听"); + mCountdownReceiver = new android.content.BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (LimitedTimeSpecialChannelService.ACTION_COUNTDOWN_TICK.equals(intent.getAction())) { + long remainingSeconds = intent.getLongExtra(LimitedTimeSpecialChannelService.EXTRA_REMAINING_SECONDS, 0); + if (mEtSeconds != null) { + mEtSeconds.setText(String.valueOf(remainingSeconds)); + } + } + } + }; + + IntentFilter filter = new IntentFilter(LimitedTimeSpecialChannelService.ACTION_COUNTDOWN_TICK); + LocalBroadcastManager.getInstance(mContext).registerReceiver(mCountdownReceiver, filter); + LogUtils.i(TAG, "倒计时广播注册成功"); } /** * 启动限时特殊通道服务 - * 读取输入框中的秒数,转换为毫秒后启动服务 */ private void startLimitedTimeSpecialChannelService() { - LogUtils.i(TAG, "检测到秒数输入框点击,准备启动服务"); - - // 1. 获取输入的秒数 + LogUtils.i(TAG, "准备启动限时特殊通道服务"); String secondsStr = mEtSeconds.getText().toString().trim(); if (secondsStr.isEmpty()) { LogUtils.w(TAG, "输入框为空,使用默认值 3600 秒"); @@ -210,43 +292,29 @@ public class LimitedTimeSpecialChannelView extends LinearLayout { } try { - // 2. 转换为毫秒数 (1秒 = 1000毫秒) long seconds = Long.parseLong(secondsStr); long delayMillis = seconds * 1000; - LogUtils.i(TAG, "输入秒数: " + seconds + " 秒,转换为毫秒: " + delayMillis); - - // 3. 调用服务启动方法 + LogUtils.i(TAG, "启动参数 - 输入秒数: " + seconds + ", 转换毫秒: " + delayMillis); 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, "反注册服务销毁本地广播完成"); -// } + // ====================== 生命周期方法 ========================= + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + 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)); -// } + if (mCountdownReceiver != null) { + LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mCountdownReceiver); + LogUtils.i(TAG, "反注册倒计时心跳广播接收器"); + } + + LogUtils.i(TAG, "所有广播反注册完成"); + } } diff --git a/contacts/src/main/res/layout/view_limitedtimespecialchannel.xml b/contacts/src/main/res/layout/view_limitedtimespecialchannel.xml index df31d4e..da6d713 100644 --- a/contacts/src/main/res/layout/view_limitedtimespecialchannel.xml +++ b/contacts/src/main/res/layout/view_limitedtimespecialchannel.xml @@ -2,29 +2,67 @@ - - - + android:gravity="center_vertical"> - + + + + + + + + + + + android:layout_height="wrap_content"> + +