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"> + + + + + + + + + + + + + + + + + + + + +