From e1428632e6b152529f2b46bb35187aecb8749153 Mon Sep 17 00:00:00 2001 From: MiMo Date: Fri, 17 Jul 2026 22:22:46 +0800 Subject: [PATCH] =?UTF-8?q?=20=E6=96=B0=E5=A2=9E=20ChargeMailNo?= =?UTF-8?q?tify=20=E5=8A=9F=E8=83=BD=E5=BC=80=E5=85=B3=E5=8F=8A=E6=82=AC?= =?UTF-8?q?=E6=B5=AE=E7=AA=97=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增 ChargeMailNotifyHelper 功能开关管理类 - 使用 SharedPreferences 持久化存储开关状态 - 提供静态方法 isEnabled() / setEnabled() 2. 新增 ChargeMailNotifyFloatWindow 悬浮窗工具类 - 单例模式(DCL双重检查锁) - show() 显示悬浮窗(带邮件图标) - dismissWindow() 静态方法供外部关闭悬浮窗 - 悬浮窗持续显示,仅在开关关闭时关闭 3. 修改 SettingsActivity 设置界面 - 新增充电邮件通知 Switch 开关(位于 SMTPConfigView 上方) - Switch 关闭时:禁用 SMTPConfigView、停止 RemindThread、关闭悬浮窗 - Switch 打开时:启用 SMTPConfigView 4. 修改 ControlCenterService 服务 - 服务启动时检查 ChargeMailNotify 功能状态 - 启用时显示悬浮窗提示充电邮件通知服务已启用 5. 修改 RemindThread 提醒线程 - 启动/停止 APPMSGMailRemindThread 前检查开关状态 - 仅在 ChargeMailNotify 启用时触发邮件提醒 --- .../activities/SettingsActivity.java | 59 ++++++++ .../services/ControlCenterService.java | 17 +++ .../powerbell/threads/RemindThread.java | 12 +- .../utils/ChargeMailNotifyFloatWindow.java | 135 ++++++++++++++++++ .../utils/ChargeMailNotifyHelper.java | 44 ++++++ .../src/main/res/layout/activity_settings.xml | 45 +++++- .../res/layout/view_charge_mail_notify.xml | 25 ++++ 7 files changed, 331 insertions(+), 6 deletions(-) create mode 100644 powerbell/src/main/java/cc/winboll/studio/powerbell/utils/ChargeMailNotifyFloatWindow.java create mode 100644 powerbell/src/main/java/cc/winboll/studio/powerbell/utils/ChargeMailNotifyHelper.java create mode 100644 powerbell/src/main/res/layout/view_charge_mail_notify.xml diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/SettingsActivity.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/SettingsActivity.java index 9e2ae8c..6748709 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/SettingsActivity.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/activities/SettingsActivity.java @@ -19,6 +19,10 @@ import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.ToastUtils; import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.models.ThoughtfulServiceBean; +import cc.winboll.studio.powerbell.utils.ChargeMailNotifyHelper; +import cc.winboll.studio.powerbell.utils.ChargeMailNotifyFloatWindow; +import cc.winboll.studio.powerbell.threads.RemindThread; +import android.widget.Switch; import java.lang.reflect.Field; /** @@ -41,6 +45,7 @@ public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivit private CheckBox cbUseageTtsBattary; // 用电TTS带电量提醒(子开关) private CheckBox cbChargeTtsBattary; // 充电TTS带电量提醒(子开关) private CheckBox cbTtsWhenNotifyBattery; // 👉 新增:通知电量时同时播放TTS + private Switch switchChargeMailNotify; // 充电邮件通知主开关 private SMTPConfigView smtpConfigView; // SMTP邮件配置视图 // ======================== 接口实现区 ========================= @@ -65,6 +70,7 @@ public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivit initTtsCheckBoxes(); initTtsCheckBoxStatus(); initSMTPConfigView(); + initChargeMailNotifySwitch(); LogUtils.d(TAG, "onCreate: 应用设置页面初始化完成"); } @@ -129,6 +135,18 @@ public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivit LogUtils.d(TAG, "initTtsCheckBoxStatus: TTS复选框状态初始化完成"); } + /** + * 初始化充电邮件通知开关 + */ + private void initChargeMailNotifySwitch() { + LogUtils.d(TAG, "initChargeMailNotifySwitch: 充电邮件通知开关初始化开始"); + switchChargeMailNotify = findViewById(R.id.switchChargeMailNotify); + boolean enabled = ChargeMailNotifyHelper.isEnabled(this); + switchChargeMailNotify.setChecked(enabled); + updateSMTPConfigViewEnabled(enabled); + LogUtils.d(TAG, "initChargeMailNotifySwitch: 充电邮件通知开关初始化完成,状态=" + enabled); + } + /** * 初始化SMTP邮件配置视图 */ @@ -138,6 +156,29 @@ public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivit LogUtils.d(TAG, "initSMTPConfigView: SMTP邮件配置视图初始化完成"); } + /** + * 更新SMTP配置视图的启用状态 + */ + private void updateSMTPConfigViewEnabled(boolean enabled) { + LogUtils.d(TAG, "updateSMTPConfigViewEnabled: 设置SMTPConfigView启用状态=" + enabled); + smtpConfigView.setEnabled(enabled); + // 递归设置子控件的启用状态 + setViewAndChildrenEnabled(smtpConfigView, enabled); + } + + /** + * 递归设置View及其所有子控件的启用状态 + */ + private void setViewAndChildrenEnabled(View view, boolean enabled) { + view.setEnabled(enabled); + if (view instanceof android.view.ViewGroup) { + android.view.ViewGroup group = (android.view.ViewGroup) view; + for (int i = 0; i < group.getChildCount(); i++) { + setViewAndChildrenEnabled(group.getChildAt(i), enabled); + } + } + } + // ======================== 事件响应区 ========================= /** * 悬浮窗权限检查入口 @@ -213,6 +254,24 @@ public class SettingsActivity extends WinBoLLActivity implements IWinBoLLActivit LogUtils.d(TAG, "onEnableTtsWhenNotifyBattery: 保存完成"); } + /** + * 充电邮件通知开关点击事件 + */ + public void onChargeMailNotifySwitchChanged(View view) { + boolean isChecked = switchChargeMailNotify.isChecked(); + LogUtils.d(TAG, "onChargeMailNotifySwitchChanged: 充电邮件通知开关状态=" + isChecked); + ChargeMailNotifyHelper.setEnabled(this, isChecked); + updateSMTPConfigViewEnabled(isChecked); + // 关闭时停止RemindThread并关闭悬浮窗 + if (!isChecked) { + LogUtils.d(TAG, "onChargeMailNotifySwitchChanged: 关闭充电邮件通知,停止RemindThread"); + RemindThread.stopRemindThread(); + LogUtils.d(TAG, "onChargeMailNotifySwitchChanged: 关闭充电邮件通知悬浮窗"); + ChargeMailNotifyFloatWindow.dismissWindow(); + } + LogUtils.d(TAG, "onChargeMailNotifySwitchChanged: 状态保存完成"); + } + // ======================== 工具方法区 ========================= /** * 获取配置Bean实例,避免重复代码 diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java index f97904d..0c301d9 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/services/ControlCenterService.java @@ -18,6 +18,8 @@ import cc.winboll.studio.powerbell.models.NotificationMessage; import cc.winboll.studio.powerbell.receivers.ControlCenterServiceReceiver; import cc.winboll.studio.powerbell.threads.RemindThread; import cc.winboll.studio.powerbell.utils.NotificationManagerUtils; +import cc.winboll.studio.powerbell.utils.ChargeMailNotifyHelper; +import cc.winboll.studio.powerbell.utils.ChargeMailNotifyFloatWindow; import java.util.List; /** @@ -220,6 +222,19 @@ public class ControlCenterService extends Service { } // ====================== 业务组件初始化与销毁(Handler/广播/线程等) ====================== + /** + * 检查充电邮件通知功能,启用时显示悬浮窗提示 + */ + private void checkChargeMailNotifyAndShowFloatWindow() { + LogUtils.d(TAG, "checkChargeMailNotifyAndShowFloatWindow() 执行"); + if (ChargeMailNotifyHelper.isEnabled(this)) { + LogUtils.d(TAG, "checkChargeMailNotifyAndShowFloatWindow() | 充电邮件通知功能已启用,显示悬浮窗"); + ChargeMailNotifyFloatWindow.getInstance(this).show(); + } else { + LogUtils.d(TAG, "checkChargeMailNotifyAndShowFloatWindow() | 充电邮件通知功能未启用,跳过悬浮窗"); + } + } + /** * 初始化Handler等核心业务组件 */ @@ -240,6 +255,8 @@ public class ControlCenterService extends Service { } else { LogUtils.d(TAG, "initServiceBusinessLogic() | 广播接收器已存在"); } + // 检查充电邮件通知功能,启用时显示悬浮窗提示 + checkChargeMailNotifyAndShowFloatWindow(); } /** diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java index 3dcc615..84e66d9 100644 --- a/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/threads/RemindThread.java @@ -6,6 +6,7 @@ import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.powerbell.App; import cc.winboll.studio.powerbell.handlers.ControlCenterServiceHandler; import cc.winboll.studio.powerbell.models.AppConfigBean; +import cc.winboll.studio.powerbell.utils.ChargeMailNotifyHelper; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -191,8 +192,10 @@ public class RemindThread extends Thread { // 启动电量通知 TTS 语音提醒线程 TTSRemindThread.start(this.mContext, App.sQuantityOfElectricity, isCharging); - // 启动电量通知邮件提醒线程 - APPMSGMailRemindThread.start(this.mContext, App.sQuantityOfElectricity, isCharging); + // 启动电量通知邮件提醒线程(仅在充电邮件通知功能启用时) + if (ChargeMailNotifyHelper.isEnabled(this.mContext)) { + APPMSGMailRemindThread.start(this.mContext, App.sQuantityOfElectricity, isCharging); + } // 安全休眠,保留中断标记 safeSleepInternal(sleepTime); @@ -286,7 +289,10 @@ public class RemindThread extends Thread { private void cleanThreadStateInternal() { LogUtils.d(TAG, String.format("cleanThreadStateInternal() 调用 | threadId=%d", getId())); TTSRemindThread.stopTTS(); - APPMSGMailRemindThread.stopMail(); + // 仅在充电邮件通知功能启用时停止邮件提醒线程 + if (ChargeMailNotifyHelper.isEnabled(this.mContext)) { + APPMSGMailRemindThread.stopMail(); + } isReminding = false; isExist = true; // 中断当前线程(如果存活) diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/ChargeMailNotifyFloatWindow.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/ChargeMailNotifyFloatWindow.java new file mode 100644 index 0000000..bd30a4f --- /dev/null +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/ChargeMailNotifyFloatWindow.java @@ -0,0 +1,135 @@ +package cc.winboll.studio.powerbell.utils; + +import android.content.Context; +import android.graphics.PixelFormat; +import android.os.Build; +import android.view.Gravity; +import android.view.View; +import android.view.WindowManager; +import cc.winboll.studio.libappbase.LogUtils; +import cc.winboll.studio.powerbell.R; + +/** + * 充电邮件通知悬浮窗工具类(单例) + * 功能:在服务启动时显示充电邮件通知已启用的悬浮提示 + * 适配:Java7 | API30 | 悬浮窗权限检查 + * @Author 豆包&ZhanGSKen + * @Date 2026年07月17日 + */ +public class ChargeMailNotifyFloatWindow { + // ====================== 常量区 ====================== + public static final String TAG = "ChargeMailNotifyFloatWindow"; + + // ====================== 单例实例 ====================== + private static volatile ChargeMailNotifyFloatWindow sInstance; + + // ====================== 成员变量 ====================== + private Context mContext; + private WindowManager mWindowManager; + private View mView; + + // ====================== 私有构造器(单例模式) ====================== + private ChargeMailNotifyFloatWindow(Context context) { + LogUtils.d(TAG, "ChargeMailNotifyFloatWindow() 构造器调用"); + this.mContext = context.getApplicationContext(); + this.mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + LogUtils.d(TAG, "ChargeMailNotifyFloatWindow() 构造完成"); + } + + // ====================== 单例获取入口(DCL双重检查锁) ====================== + public static ChargeMailNotifyFloatWindow getInstance(Context context) { + if (sInstance == null) { + synchronized (ChargeMailNotifyFloatWindow.class) { + if (sInstance == null) { + sInstance = new ChargeMailNotifyFloatWindow(context); + } + } + } + return sInstance; + } + + // ====================== 对外公开方法 ====================== + /** + * 显示充电邮件通知悬浮窗 + */ + public void show() { + LogUtils.d(TAG, "show() 调用"); + + // 检查悬浮窗权限 + if (!checkOverlayPermission()) { + LogUtils.w(TAG, "show() 悬浮窗权限未授予,跳过显示"); + return; + } + + // 清理已存在的悬浮窗 + dismiss(); + + try { + // 创建Window布局参数 + WindowManager.LayoutParams params = new WindowManager.LayoutParams(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + } else { + params.type = WindowManager.LayoutParams.TYPE_PHONE; + } + params.alpha = 0.9f; + params.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; + params.x = 0; + params.y = 100; + params.format = PixelFormat.RGBA_8888; + params.width = WindowManager.LayoutParams.WRAP_CONTENT; + params.height = WindowManager.LayoutParams.WRAP_CONTENT; + params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; + + // 加载悬浮窗布局 + mView = View.inflate(mContext, R.layout.view_charge_mail_notify, null); + mWindowManager.addView(mView, params); + LogUtils.d(TAG, "show() 悬浮窗显示成功"); + } catch (Exception e) { + LogUtils.e(TAG, "show() 悬浮窗显示异常", e); + mView = null; + } + } + + /** + * 关闭悬浮窗(实例方法) + */ + public void dismiss() { + LogUtils.d(TAG, "dismiss() 调用"); + + if (mWindowManager != null && mView != null) { + try { + mWindowManager.removeView(mView); + LogUtils.d(TAG, "dismiss() 悬浮窗移除成功"); + } catch (Exception e) { + LogUtils.e(TAG, "dismiss() 悬浮窗移除异常", e); + } finally { + mView = null; + } + } + } + + /** + * 关闭悬浮窗(静态公共方法,供外部调用) + */ + public static void dismissWindow() { + LogUtils.d(TAG, "dismissWindow() 静态方法调用"); + if (sInstance != null) { + sInstance.dismiss(); + } + } + + // ====================== 私有辅助方法 ====================== + /** + * 检查悬浮窗权限 + */ + private boolean checkOverlayPermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + boolean hasPermission = android.provider.Settings.canDrawOverlays(mContext); + LogUtils.d(TAG, "checkOverlayPermission() 权限校验结果=" + hasPermission); + return hasPermission; + } + return true; + } +} diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/ChargeMailNotifyHelper.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/ChargeMailNotifyHelper.java new file mode 100644 index 0000000..39ec79f --- /dev/null +++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/ChargeMailNotifyHelper.java @@ -0,0 +1,44 @@ +package cc.winboll.studio.powerbell.utils; + +import android.content.Context; +import android.content.SharedPreferences; +import cc.winboll.studio.libappbase.LogUtils; + +/** + * 充电邮件通知功能开关管理类 + * 适配 API30,基于 Java7 开发 + * @Author 豆包&ZhanGSKen + * @Date 2026年07月17日 + * @Describe 管理充电邮件通知功能的启用/关闭状态,使用SP持久化存储 + */ +public class ChargeMailNotifyHelper { + // ======================== 静态常量区 ========================= + private static final String TAG = "ChargeMailNotifyHelper"; + private static final String SP_NAME = "charge_mail_notify_config"; + private static final String SP_KEY_ENABLED = "charge_mail_notify_enabled"; + private static final boolean DEFAULT_ENABLED = false; + + // ======================== 静态方法区 ========================= + /** + * 获取充电邮件通知功能是否启用 + * @param context 上下文 + * @return true=已启用,false=已关闭 + */ + public static boolean isEnabled(Context context) { + SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + boolean enabled = sp.getBoolean(SP_KEY_ENABLED, DEFAULT_ENABLED); + LogUtils.d(TAG, "isEnabled: " + enabled); + return enabled; + } + + /** + * 设置充电邮件通知功能启用/关闭状态 + * @param context 上下文 + * @param enabled true=启用,false=关闭 + */ + public static void setEnabled(Context context, boolean enabled) { + SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); + sp.edit().putBoolean(SP_KEY_ENABLED, enabled).apply(); + LogUtils.d(TAG, "setEnabled: " + enabled); + } +} diff --git a/powerbell/src/main/res/layout/activity_settings.xml b/powerbell/src/main/res/layout/activity_settings.xml index c8038f7..b2a4c6b 100644 --- a/powerbell/src/main/res/layout/activity_settings.xml +++ b/powerbell/src/main/res/layout/activity_settings.xml @@ -153,11 +153,50 @@ - + android:layout_marginTop="10dp" + android:orientation="vertical" + android:background="@drawable/bg_frame" + android:padding="10dp"> + + + + + + + + + + + + + + + + + + + + +