diff --git a/powerbell/build.properties b/powerbell/build.properties
index c9e6bc1..d637350 100644
--- a/powerbell/build.properties
+++ b/powerbell/build.properties
@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
-#Thu Dec 11 16:22:39 GMT 2025
+#Sat Dec 13 12:30:20 GMT 2025
stageCount=16
libraryProject=
baseVersion=15.12
publishVersion=15.12.15
-buildCount=1
+buildCount=3
baseBetaVersion=15.12.16
diff --git a/powerbell/src/main/AndroidManifest.xml b/powerbell/src/main/AndroidManifest.xml
index 76c7e88..1f88863 100644
--- a/powerbell/src/main/AndroidManifest.xml
+++ b/powerbell/src/main/AndroidManifest.xml
@@ -4,12 +4,6 @@
xmlns:tools="http://schemas.android.com/tools"
package="cc.winboll.studio.powerbell">
-
-
-
-
-
-
@@ -143,14 +137,14 @@
@@ -190,14 +184,14 @@
@@ -206,13 +200,13 @@
android:name="android.max_aspect"
android:value="4.0"/>
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
+
+
+
diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationHelper.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationHelper.java
deleted file mode 100644
index 011b028..0000000
--- a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationHelper.java
+++ /dev/null
@@ -1,151 +0,0 @@
-package cc.winboll.studio.powerbell.utils;
-
-/**
- * @Author ZhanGSKen
- * @Date 2025/03/22 04:39:40
- * @Describe 通知工具类
- */
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.BitmapFactory;
-import android.os.Build;
-import android.widget.RemoteViews;
-import androidx.annotation.RequiresApi;
-import androidx.core.app.NotificationCompat;
-import cc.winboll.studio.powerbell.R;
-
-public class NotificationHelper {
- public static final String TAG = "NotificationHelper";
-
- // 渠道ID和名称
- private static final String CHANNEL_ID_FOREGROUND = "foreground_channel";
- private static final String CHANNEL_NAME_FOREGROUND = "Foreground Service";
- private static final String CHANNEL_ID_TEMPORARY = "temporary_channel";
- private static final String CHANNEL_NAME_TEMPORARY = "Temporary Notifications";
-
- // 通知ID
- public static final int FOREGROUND_NOTIFICATION_ID = 1001;
- public static final int TEMPORARY_NOTIFICATION_ID = 2001;
-
- private final Context mContext;
- private final NotificationManager mNotificationManager;
-
- public NotificationHelper(Context context) {
- mContext = context;
- mNotificationManager = context.getSystemService(NotificationManager.class);
- createNotificationChannels();
- }
-
- @RequiresApi(api = Build.VERSION_CODES.O)
- private void createNotificationChannels() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- createForegroundChannel();
- createTemporaryChannel();
- }
- }
-
- @RequiresApi(api = Build.VERSION_CODES.O)
- private void createForegroundChannel() {
- NotificationChannel channel = new NotificationChannel(
- CHANNEL_ID_FOREGROUND,
- CHANNEL_NAME_FOREGROUND,
- NotificationManager.IMPORTANCE_LOW
- );
- channel.setDescription("Persistent service notifications");
- channel.setSound(null, null);
- channel.enableVibration(false);
- mNotificationManager.createNotificationChannel(channel);
- }
-
- @RequiresApi(api = Build.VERSION_CODES.O)
- private void createTemporaryChannel() {
- NotificationChannel channel = new NotificationChannel(
- CHANNEL_ID_TEMPORARY,
- CHANNEL_NAME_TEMPORARY,
- NotificationManager.IMPORTANCE_HIGH
- );
- channel.setDescription("Temporary alert notifications");
- channel.setSound(null, null);
- channel.enableVibration(true);
- channel.setVibrationPattern(new long[]{100, 200, 300, 400});
- channel.setBypassDnd(true);
- mNotificationManager.createNotificationChannel(channel);
- }
-
- // 显示常驻通知(通常用于前台服务)
- public Notification showForegroundNotification(Intent intent, String title, String content) {
- PendingIntent pendingIntent = createPendingIntent(intent);
-
- Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_FOREGROUND)
- .setSmallIcon(R.drawable.ic_launcher)
- .setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
- //.setContentTitle(title + "\n" + content)
- .setContentTitle(content)
- //.setContentText(content)
- .setContentIntent(pendingIntent)
- .setPriority(NotificationCompat.PRIORITY_LOW)
- .setOngoing(true)
- .build();
-
- mNotificationManager.notify(FOREGROUND_NOTIFICATION_ID, notification);
- return notification;
- }
-
- // 显示临时通知(自动消失)
- public void showTemporaryNotification(Intent intent, String title, String content) {
- PendingIntent pendingIntent = createPendingIntent(intent);
-
- Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMPORARY)
- .setSmallIcon(R.drawable.ic_launcher)
- .setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
- .setContentTitle(title)
- .setContentText(content)
- .setContentIntent(pendingIntent)
- .setPriority(NotificationCompat.PRIORITY_HIGH)
- .setAutoCancel(true)
- .setVibrate(new long[]{100, 200, 300, 400})
- .build();
-
- mNotificationManager.notify(TEMPORARY_NOTIFICATION_ID, notification);
- }
-
- // 创建自定义布局通知(可扩展)
- public void showCustomNotification(Intent intent, RemoteViews contentView, RemoteViews bigContentView) {
- PendingIntent pendingIntent = createPendingIntent(intent);
-
- Notification notification = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMPORARY)
- .setSmallIcon(R.drawable.ic_launcher)
- .setContentIntent(pendingIntent)
- .setContent(contentView)
- .setCustomBigContentView(bigContentView)
- .setPriority(NotificationCompat.PRIORITY_HIGH)
- .setAutoCancel(true)
- .build();
-
- mNotificationManager.notify(TEMPORARY_NOTIFICATION_ID + 1, notification);
- }
-
- // 取消所有通知
- public void cancelAllNotifications() {
- mNotificationManager.cancelAll();
- }
-
- // 创建PendingIntent(兼容不同API版本)
- private PendingIntent createPendingIntent(Intent intent) {
- int flags = PendingIntent.FLAG_UPDATE_CURRENT;
-// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
-// flags |= PendingIntent.FLAG_IMMUTABLE;
-// }
- return PendingIntent.getActivity(
- mContext,
- 0,
- intent,
- flags
- );
- }
-}
-
diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationManagerUtils.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationManagerUtils.java
new file mode 100644
index 0000000..273c9d7
--- /dev/null
+++ b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationManagerUtils.java
@@ -0,0 +1,422 @@
+package cc.winboll.studio.powerbell.utils;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.media.RingtoneManager;
+import android.os.Build;
+import android.view.View;
+import android.widget.RemoteViews;
+import androidx.core.app.NotificationCompat;
+import cc.winboll.studio.libappbase.LogUtils;
+import cc.winboll.studio.powerbell.MainActivity;
+import cc.winboll.studio.powerbell.R;
+import cc.winboll.studio.powerbell.models.NotificationMessage;
+import cc.winboll.studio.powerbell.services.ControlCenterService;
+
+/**
+ * @Author ZhanGSKen&豆包大模型
+ * @Date 2025/12/13 20:44
+ * @Describe 全局通知管理工具类(整合所有通知能力,适配API29-30,兼容Java7)
+ */
+public class NotificationManagerUtils {
+ // ====================== 常量定义(统一管理,避免冲突,首屏可见)======================
+ public static final String TAG = "NotificationManagerUtils";
+ // 通知渠道(4大渠道,场景隔离,API26+必填)
+ // 1. 前台服务保活渠道(低优先级,无打扰)
+ private static final String CHANNEL_ID_FOREGROUND_SERVICE = "channel_foreground_service";
+ private static final String CHANNEL_NAME_FOREGROUND_SERVICE = "前台服务保活通知";
+ private static final String CHANNEL_DESC_FOREGROUND_SERVICE = "后台服务运行状态,无声音无震动,不打扰用户";
+ // 2. 电量提醒渠道(高优先级,闹钟铃声+震动,强提醒)
+ private static final String CHANNEL_ID_BATTERY_REMIND = "channel_battery_remind";
+ private static final String CHANNEL_NAME_BATTERY_REMIND = "电量异常提醒通知";
+ private static final String CHANNEL_DESC_BATTERY_REMIND = "电量过高/过低提醒,强震动+闹钟铃声,突破免打扰";
+ // 3. 通用临时通知渠道(高优先级,仅震动,普通告警)
+ private static final String CHANNEL_ID_TEMP_ALERT = "channel_temp_alert";
+ private static final String CHANNEL_NAME_TEMP_ALERT = "通用临时提醒通知";
+ private static final String CHANNEL_DESC_TEMP_ALERT = "普通即时告警,仅震动提醒,自动取消";
+ // 通知ID(唯一区分,避免覆盖,按场景分段)
+ public static final int NOTIFY_ID_FOREGROUND_SERVICE = 1001; // 前台服务
+ public static final int NOTIFY_ID_BATTERY_REMIND = 1002; // 电量提醒
+ public static final int NOTIFY_ID_TEMP_ALERT = 1003; // 通用临时通知
+ public static final int NOTIFY_ID_CUSTOM_LAYOUT = 1004; // 自定义布局通知
+ // 通用配置
+ private static final int PENDING_INTENT_FLAGS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+ ? PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
+ : PendingIntent.FLAG_UPDATE_CURRENT; // API30安全标志
+ private static final long[] VIBRATE_PATTERN = new long[]{100, 200, 300, 400}; // 标准震动节奏
+ private static final String DEFAULT_JUMP_PACKAGE = "cc.winboll.studio.powerbell"; // 默认跳转包名(API29+必填)
+
+ // ====================== 成员变量(按场景分组,私有封装,避免外部篡改)======================
+ private final Context mContext;
+ private final NotificationManager mNotificationManager;
+ // 前台服务通知专属
+ private Notification mForegroundServiceNotify;
+ private RemoteViews mForegroundServiceRemoteViews;
+ // 电量提醒通知专属
+ private Notification mBatteryRemindNotify;
+ private RemoteViews mBatteryRemindRemoteViews;
+
+ // ====================== 构造方法(单例思想/实例化通用,自动初始化渠道)======================
+ public NotificationManagerUtils(Context context) {
+ LogUtils.d(TAG, "【初始化】全局通知管理工具类 构造方法调用");
+ this.mContext = context.getApplicationContext(); // 用应用上下文,避免内存泄漏
+ this.mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ createAllNotificationChannels(); // 自动创建所有渠道(API26+)
+ LogUtils.d(TAG, "【初始化】全局通知管理工具类 完成,渠道创建状态:" + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? "已创建4个渠道" : "无需创建"));
+ }
+
+ // ====================== 核心基础能力(渠道创建+Intent构建,复用逻辑,减少冗余)======================
+ /**
+ * 创建所有通知渠道(API26+专属,低版本自动跳过,确保通知正常显示)
+ */
+ @SuppressWarnings("deprecation")
+ public void createAllNotificationChannels() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ LogUtils.d(TAG, "【渠道管理】开始创建所有通知渠道");
+ createForegroundServiceChannel();
+ createBatteryRemindChannel();
+ createTempAlertChannel();
+ LogUtils.d(TAG, "【渠道管理】4个通知渠道创建完成(含3个核心渠道+预留扩展)");
+ }
+ }
+
+ /**
+ * 创建前台服务保活渠道(IMPORTANCE_LOW,无声音无震动,不打扰用户)
+ */
+ private void createForegroundServiceChannel() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel channel = new NotificationChannel(
+ CHANNEL_ID_FOREGROUND_SERVICE,
+ CHANNEL_NAME_FOREGROUND_SERVICE,
+ NotificationManager.IMPORTANCE_LOW
+ );
+ channel.setDescription(CHANNEL_DESC_FOREGROUND_SERVICE);
+ channel.setSound(null, null);
+ channel.enableVibration(false);
+ channel.setShowBadge(false); // 不显示应用角标
+ channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); // 锁屏隐藏
+ mNotificationManager.createNotificationChannel(channel);
+ LogUtils.d(TAG, "【渠道管理】前台服务保活渠道创建成功:" + CHANNEL_NAME_FOREGROUND_SERVICE);
+ }
+ }
+
+ /**
+ * 创建电量提醒渠道(IMPORTANCE_HIGH,闹钟铃声+震动,突破免打扰)
+ */
+ private void createBatteryRemindChannel() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel channel = new NotificationChannel(
+ CHANNEL_ID_BATTERY_REMIND,
+ CHANNEL_NAME_BATTERY_REMIND,
+ NotificationManager.IMPORTANCE_HIGH
+ );
+ channel.setDescription(CHANNEL_DESC_BATTERY_REMIND);
+ channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM), null); // 闹钟铃声
+ channel.enableVibration(true);
+ channel.setVibrationPattern(VIBRATE_PATTERN);
+ channel.setBypassDnd(true); // 突破免打扰
+ channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); // 锁屏可见
+ channel.setShowBadge(true);
+ mNotificationManager.createNotificationChannel(channel);
+ LogUtils.d(TAG, "【渠道管理】电量提醒渠道创建成功:" + CHANNEL_NAME_BATTERY_REMIND);
+ }
+ }
+
+ /**
+ * 创建通用临时通知渠道(IMPORTANCE_HIGH,仅震动,普通告警)
+ */
+ private void createTempAlertChannel() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel channel = new NotificationChannel(
+ CHANNEL_ID_TEMP_ALERT,
+ CHANNEL_NAME_TEMP_ALERT,
+ NotificationManager.IMPORTANCE_HIGH
+ );
+ channel.setDescription(CHANNEL_DESC_TEMP_ALERT);
+ channel.setSound(null, null); // 仅震动,不发声
+ channel.enableVibration(true);
+ channel.setVibrationPattern(VIBRATE_PATTERN);
+ channel.setBypassDnd(false); // 不突破免打扰
+ channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
+ channel.setShowBadge(true);
+ mNotificationManager.createNotificationChannel(channel);
+ LogUtils.d(TAG, "【渠道管理】通用临时通知渠道创建成功:" + CHANNEL_NAME_TEMP_ALERT);
+ }
+ }
+
+ /**
+ * 构建通用跳转PendingIntent(适配API29-30安全规范,支持自定义跳转目标)
+ * @param targetIntent 自定义跳转意图(传null则默认跳MainActivity)
+ * @return 安全的PendingIntent,避免跳转失败/泄露
+ */
+ private PendingIntent buildPendingIntent(Intent targetIntent) {
+ Intent intent = targetIntent;
+ // 传null则默认跳MainActivity(兼容旧逻辑)
+ if (intent == null) {
+ intent = new Intent(mContext, MainActivity.class);
+ LogUtils.d(TAG, "【Intent构建】未传自定义Intent,默认跳转:MainActivity");
+ }
+ // API29+ 强制要求:明确包名,避免跳转目标模糊
+ intent.setPackage(DEFAULT_JUMP_PACKAGE);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 确保跳转生效
+ LogUtils.d(TAG, "【Intent构建】跳转包名:" + DEFAULT_JUMP_PACKAGE + ",目标:" + intent.getComponent().getClassName());
+
+ PendingIntent pendingIntent = PendingIntent.getActivity(
+ mContext,
+ 0,
+ intent,
+ PENDING_INTENT_FLAGS
+ );
+ LogUtils.d(TAG, "【Intent构建】PendingIntent创建成功,安全标志:" + PENDING_INTENT_FLAGS);
+ return pendingIntent;
+ }
+
+ // ====================== 场景1:前台服务保活通知(支持自定义布局+更新)======================
+ /**
+ * 初始化前台服务通知自定义布局(RemoteViews)
+ */
+ private void initForegroundServiceRemoteViews(ControlCenterService service, NotificationMessage msg) {
+ LogUtils.d(TAG, "【布局初始化】开始初始化前台服务通知布局,标题:" + msg.getTitle());
+ mForegroundServiceRemoteViews = new RemoteViews(service.getPackageName(), R.layout.view_servicenotification);
+ mForegroundServiceRemoteViews.setTextViewText(R.id.remoteviewTextView1, msg.getTitle());
+ mForegroundServiceRemoteViews.setTextViewText(R.id.remoteviewTextView3, msg.getContent());
+ mForegroundServiceRemoteViews.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
+ LogUtils.d(TAG, "【布局初始化】前台服务通知布局填充完成");
+ }
+
+ /**
+ * 启动前台服务保活通知(ControlCenterService专用,API26+强制要求,保活后台服务)
+ */
+ public void startForegroundServiceNotify(ControlCenterService service, NotificationMessage msg) {
+ LogUtils.d(TAG, "【前台服务通知】开始构建保活通知,内容:" + msg.getContent());
+ if (service == null || msg == null) {
+ LogUtils.e(TAG, "【前台服务通知】构建失败:Service/NotificationMessage为空");
+ return;
+ }
+
+ // 1. 构建跳转Intent
+ PendingIntent pendingIntent = buildPendingIntent(null); // 默认跳MainActivity
+ // 2. 构建基础通知(兼容API26+渠道,低版本用Builder)
+ Notification.Builder builder;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ builder = new Notification.Builder(service, CHANNEL_ID_FOREGROUND_SERVICE);
+ } else {
+ builder = new Notification.Builder(service);
+ }
+ mForegroundServiceNotify = builder
+ .setSmallIcon(R.drawable.ic_launcher)
+ .setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher))
+ .setContentTitle(msg.getTitle())
+ .setContentText(msg.getContent())
+ .setWhen(System.currentTimeMillis())
+ .setColor(Color.parseColor("#F00606")) // 小图标背景色
+ .setContentIntent(pendingIntent)
+ .setOngoing(true) // 常驻通知,不可滑动取消(保活关键)
+ .setAutoCancel(false) // 禁止点击取消
+ .build();
+ // 3. 设置自定义布局
+ initForegroundServiceRemoteViews(service, msg);
+ mForegroundServiceNotify.contentView = mForegroundServiceRemoteViews;
+ mForegroundServiceNotify.bigContentView = mForegroundServiceRemoteViews;
+ // 4. 启动前台服务(必须调用,否则Service易被回收)
+ service.startForeground(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
+ LogUtils.d(TAG, "【前台服务通知】保活通知启动成功,通知ID:" + NOTIFY_ID_FOREGROUND_SERVICE);
+ }
+
+ /**
+ * 更新前台服务保活通知内容(无需重启服务,直接刷新布局)
+ */
+ public void updateForegroundServiceNotify(ControlCenterService service, NotificationMessage msg) {
+ LogUtils.d(TAG, "【前台服务通知】开始更新保活通知,新内容:" + msg.getContent());
+ if (mForegroundServiceNotify == null || mForegroundServiceRemoteViews == null) {
+ LogUtils.e(TAG, "【前台服务通知】更新失败:通知对象未初始化,先调用startForegroundServiceNotify");
+ return;
+ }
+ // 更新自定义布局数据
+ initForegroundServiceRemoteViews(service, msg);
+ mForegroundServiceNotify.contentView = mForegroundServiceRemoteViews;
+ mForegroundServiceNotify.bigContentView = mForegroundServiceRemoteViews;
+ // 发送更新
+ mNotificationManager.notify(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
+ LogUtils.d(TAG, "【前台服务通知】保活通知更新成功");
+ }
+
+ // ====================== 场景2:电量提醒通知(支持自定义布局+更新+单独取消)======================
+ /**
+ * 初始化电量提醒通知自定义布局(RemoteViews,支持充电/耗电切换)
+ */
+ private void initBatteryRemindRemoteViews(ControlCenterService service, NotificationMessage msg) {
+ LogUtils.d(TAG, "【布局初始化】开始初始化电量提醒布局,提醒类型:" + msg.getRemindMSG());
+ mBatteryRemindRemoteViews = new RemoteViews(service.getPackageName(), R.layout.view_remindnotification);
+ mBatteryRemindRemoteViews.setTextViewText(R.id.viewremindnotificationTextView1, msg.getTitle());
+ mBatteryRemindRemoteViews.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
+ // 切换布局(+:充电提醒,-:耗电提醒)
+ String remindType = msg.getRemindMSG() != null ? msg.getRemindMSG().trim() : "";
+ if ("+".equals(remindType)) {
+ mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewUsege, View.GONE);
+ mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewCharge, View.VISIBLE);
+ LogUtils.d(TAG, "【布局初始化】电量提醒布局切换:充电提醒");
+ } else if ("-".equals(remindType)) {
+ mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewCharge, View.GONE);
+ mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewUsege, View.VISIBLE);
+ LogUtils.d(TAG, "【布局初始化】电量提醒布局切换:耗电提醒");
+ } else {
+ mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewCharge, View.GONE);
+ mBatteryRemindRemoteViews.setViewVisibility(R.id.remoteviewUsege, View.VISIBLE);
+ LogUtils.w(TAG, "【布局初始化】未知电量提醒类型:" + remindType);
+ }
+ }
+
+ /**
+ * 初始化电量提醒通知(仅构建,不发送,配合update触发提醒)
+ */
+ public void initBatteryRemindNotify(ControlCenterService service, NotificationMessage msg) {
+ LogUtils.d(TAG, "【电量提醒通知】开始初始化提醒通知,标题:" + msg.getTitle());
+ if (service == null || msg == null) {
+ LogUtils.e(TAG, "【电量提醒通知】初始化失败:Service/NotificationMessage为空");
+ return;
+ }
+ // 1. 构建跳转Intent
+ PendingIntent pendingIntent = buildPendingIntent(null);
+ // 2. 构建基础通知
+ Notification.Builder builder;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ builder = new Notification.Builder(service, CHANNEL_ID_BATTERY_REMIND);
+ } else {
+ builder = new Notification.Builder(service);
+ }
+ mBatteryRemindNotify = builder
+ .setSmallIcon(R.drawable.ic_launcher)
+ .setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher))
+ .setContentTitle(msg.getTitle())
+ .setContentText(msg.getContent())
+ .setWhen(System.currentTimeMillis())
+ .setColor(Color.parseColor("#F00606"))
+ .setContentIntent(pendingIntent)
+ .setAutoCancel(true) // 点击取消
+ .build();
+ // 3. 初始化自定义布局
+ initBatteryRemindRemoteViews(service, msg);
+ mBatteryRemindNotify.contentView = mBatteryRemindRemoteViews;
+ mBatteryRemindNotify.bigContentView = mBatteryRemindRemoteViews;
+ LogUtils.d(TAG, "【电量提醒通知】初始化完成");
+ }
+
+ /**
+ * 发送/更新电量提醒通知(初始化后调用,触发强提醒)
+ */
+ public void sendOrUpdateBatteryRemindNotify() {
+ LogUtils.d(TAG, "【电量提醒通知】开始发送/更新提醒");
+ if (mBatteryRemindNotify == null || mBatteryRemindRemoteViews == null) {
+ LogUtils.e(TAG, "【电量提醒通知】发送失败:通知未初始化,先调用initBatteryRemindNotify");
+ return;
+ }
+ mNotificationManager.notify(NOTIFY_ID_BATTERY_REMIND, mBatteryRemindNotify);
+ LogUtils.d(TAG, "【电量提醒通知】发送/更新成功,通知ID:" + NOTIFY_ID_BATTERY_REMIND);
+ }
+
+ /**
+ * 单独取消电量提醒通知(静态方法,外部可直接调用,无需实例化)
+ */
+ public static void cancelBatteryRemindNotify(Context context) {
+ LogUtils.d(TAG, "【电量提醒通知】开始取消提醒,通知ID:" + NOTIFY_ID_BATTERY_REMIND);
+ if (context == null) {
+ LogUtils.e(TAG, "【电量提醒通知】取消失败:Context为空");
+ return;
+ }
+ NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ manager.cancel(NOTIFY_ID_BATTERY_REMIND);
+ LogUtils.d(TAG, "【电量提醒通知】取消成功");
+ }
+
+ // ====================== 场景3:通用临时通知(简单文本,自动取消,无需自定义布局)======================
+ /**
+ * 显示通用临时通知(普通告警,仅震动,自动取消,支持自定义跳转目标)
+ * @param targetIntent 自定义跳转Intent(传null默认跳MainActivity)
+ * @param title 通知标题
+ * @param content 通知内容
+ */
+ public void showTempAlertNotify(Intent targetIntent, String title, String content) {
+ LogUtils.d(TAG, "【通用临时通知】开始构建,标题:" + title + ",内容:" + content);
+ if (title == null || content == null) {
+ LogUtils.e(TAG, "【通用临时通知】构建失败:标题/内容为空");
+ return;
+ }
+ // 1. 构建跳转Intent
+ PendingIntent pendingIntent = buildPendingIntent(targetIntent);
+ // 2. 用NotificationCompat.Builder(兼容所有版本,简化逻辑)
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMP_ALERT)
+ .setSmallIcon(R.drawable.ic_launcher)
+ .setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher))
+ .setContentTitle(title)
+ .setContentText(content)
+ .setWhen(System.currentTimeMillis())
+ .setContentIntent(pendingIntent)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setAutoCancel(true)
+ .setVibrate(VIBRATE_PATTERN);
+ // 3. 发送通知
+ Notification notification = builder.build();
+ mNotificationManager.notify(NOTIFY_ID_TEMP_ALERT, notification);
+ LogUtils.d(TAG, "【通用临时通知】显示成功,通知ID:" + NOTIFY_ID_TEMP_ALERT);
+ }
+
+ // ====================== 场景4:自定义布局通知(灵活扩展,支持复杂样式)======================
+ /**
+ * 显示自定义布局通知(支持普通布局+大布局,通用所有场景,可自定义跳转)
+ * @param targetIntent 自定义跳转Intent
+ * @param contentView 普通自定义布局(必填)
+ * @param bigContentView 下拉大布局(可选)
+ */
+ public void showCustomLayoutNotify(Intent targetIntent, RemoteViews contentView, RemoteViews bigContentView) {
+ LogUtils.d(TAG, "【自定义布局通知】开始构建,布局ID:" + (contentView != null ? contentView.getLayoutId() : "null"));
+ if (contentView == null) {
+ LogUtils.e(TAG, "【自定义布局通知】构建失败:普通布局contentView为空");
+ return;
+ }
+ // 1. 构建跳转Intent
+ PendingIntent pendingIntent = buildPendingIntent(targetIntent);
+ // 2. 构建自定义布局通知
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, CHANNEL_ID_TEMP_ALERT)
+ .setSmallIcon(R.drawable.ic_launcher) // 必传,不可省略
+ .setContentIntent(pendingIntent)
+ .setContent(contentView)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setAutoCancel(true);
+ // 添加大布局(可选)
+ if (bigContentView != null) {
+ builder.setCustomBigContentView(bigContentView);
+ LogUtils.d(TAG, "【自定义布局通知】已添加下拉大布局,布局ID:" + bigContentView.getLayoutId());
+ }
+ // 3. 发送通知
+ Notification notification = builder.build();
+ mNotificationManager.notify(NOTIFY_ID_CUSTOM_LAYOUT, notification);
+ LogUtils.d(TAG, "【自定义布局通知】显示成功,通知ID:" + NOTIFY_ID_CUSTOM_LAYOUT);
+ }
+
+ // ====================== 通知取消工具(支持精准取消/全取消)======================
+ /**
+ * 取消指定ID的通知(精准取消,灵活控制)
+ */
+ public void cancelNotifyById(int notifyId) {
+ LogUtils.d(TAG, "【通知管理】开始取消通知,ID:" + notifyId);
+ mNotificationManager.cancel(notifyId);
+ LogUtils.d(TAG, "【通知管理】通知取消成功,ID:" + notifyId);
+ }
+
+ /**
+ * 取消所有通知(谨慎使用,会清除所有场景的通知)
+ */
+ public void cancelAllNotifies() {
+ LogUtils.d(TAG, "【通知管理】开始取消所有通知");
+ mNotificationManager.cancelAll();
+ LogUtils.d(TAG, "【通知管理】所有通知取消完成");
+ }
+}
+
diff --git a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationUtils2.java b/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationUtils2.java
deleted file mode 100644
index c07278f..0000000
--- a/powerbell/src/main/java/cc/winboll/studio/powerbell/utils/NotificationUtils2.java
+++ /dev/null
@@ -1,247 +0,0 @@
-package cc.winboll.studio.powerbell.utils;
-
-/*
- * 参考:
- * https://blog.csdn.net/qq_35507234/article/details/90676587
- * https://blog.csdn.net/qq_16628781/article/details/51548324
- */
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.media.RingtoneManager;
-import android.os.Build;
-import android.view.View;
-import android.widget.RemoteViews;
-import androidx.annotation.RequiresApi;
-import cc.winboll.studio.powerbell.MainActivity;
-import cc.winboll.studio.powerbell.R;
-import cc.winboll.studio.powerbell.models.NotificationMessage;
-import cc.winboll.studio.powerbell.services.ControlCenterService;
-
-public class NotificationUtils2 {
-
- public static final String TAG = NotificationHelper.class.getSimpleName();
-
- Context mContext;
- NotificationManager mNotificationManager;
-
- Notification mForegroundNotification;
- PendingIntent mForegroundPendingIntent;
- Notification mRemindNotification;
- PendingIntent mRemindPendingIntent;
- RemoteViews mrvServiceNotificationView;
- RemoteViews mrvRemindNotificationView;
-
- static enum NotificationType { MIN, MAX };
- private static int _mnServiceNotificationID = 1;
- private static int _mnRemindNotificationID = 2;
- private static String _mszChannelIDService = "1";
- private static String _mszChannelNameService = "Service";
- private static String _mszChannelIDRemind = "2";
- private static String _mszChannelNameRemind = "Remind";
-
-// public NotificationUtils(Context context) {
-// mContext = context;
-// mNotificationManager = (NotificationManager) context.getSystemService(
-// Context.NOTIFICATION_SERVICE);
-// }
-
- public NotificationUtils2(Context context) {
- mContext = context;
- mNotificationManager = context.getSystemService(NotificationManager.class);
- //createNotificationChannels();
- }
-
- @RequiresApi(api = Build.VERSION_CODES.O)
- public void createNotificationChannels() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- createServiceChannel();
- createRemindChannel();
- }
- }
-
- @RequiresApi(api = Build.VERSION_CODES.O)
- private void createServiceChannel() {
- NotificationChannel channel = new NotificationChannel(
- _mszChannelIDService,
- _mszChannelNameService,
- NotificationManager.IMPORTANCE_LOW
- );
- channel.setDescription("Background service updates");
- channel.setSound(null, null);
- channel.enableVibration(false);
- mNotificationManager.createNotificationChannel(channel);
- }
-
- @RequiresApi(api = Build.VERSION_CODES.O)
- private void createRemindChannel() {
- NotificationChannel channel = new NotificationChannel(
- _mszChannelIDRemind,
- _mszChannelNameRemind,
- NotificationManager.IMPORTANCE_HIGH
- );
- channel.setDescription("Critical reminders");
- channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM), null);
- channel.enableVibration(true);
- channel.setVibrationPattern(new long[]{100, 200, 300, 400});
- channel.setBypassDnd(true);
- channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
- mNotificationManager.createNotificationChannel(channel);
- }
-
- // 创建并发送服务通知
- //
- public void createForegroundNotification(ControlCenterService service, NotificationMessage notificationMessage) {
- //创建Notification,传入Context和channelId
- Intent intent = new Intent();//这个intent会传给目标,可以使用getIntent来获取
- intent.setPackage(service.getPackageName());
- //LogUtils.d(TAG, "mService.getPackageName() : " + service.getPackageName());
- intent.setClass(service, MainActivity.class);
- //LogUtils.d(TAG, "MainActivity.class.getName() : " + MainActivity.class.getName());
- //这里放一个count用来区分每一个通知
- //intent.putExtra("intent", "intent--->" + count);//这里设置一个数据,带过去
-
- //参数1:context 上下文对象
- //参数2:发送者私有的请求码(Private request code for the sender)
- //参数3:intent 意图对象
- //参数4:必须为FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT,FLAG_UPDATE_CURRENT,中的一个
- //mForegroundPendingIntent = PendingIntent.getActivity(mService, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- mForegroundPendingIntent = PendingIntent.getActivity(service,
- 1, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- } else {
- mForegroundPendingIntent = PendingIntent.getActivity(service,
- 1, intent, PendingIntent.FLAG_IMMUTABLE);
- }
-
- mForegroundNotification = new Notification.Builder(service, _mszChannelIDService)
- .setAutoCancel(true)
- .setContentTitle(notificationMessage.getTitle())
- .setContentText(notificationMessage.getContent())
- .setWhen(System.currentTimeMillis())
- .setSmallIcon(R.drawable.ic_launcher)
- //设置红色
- .setColor(Color.parseColor("#F00606"))
- .setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher))
- .setContentIntent(mForegroundPendingIntent)
- .build();
-
- setForegroundNotificationRemoteViews(service, notificationMessage);
- service.startForeground(_mnServiceNotificationID, mForegroundNotification);
- }
-
- void initmrvRemindNotificationView(ControlCenterService service, NotificationMessage notificationMessage) {
- mrvRemindNotificationView = new RemoteViews(service.getPackageName(), R.layout.view_remindnotification);
- mrvRemindNotificationView.setTextViewText(R.id.viewremindnotificationTextView1, notificationMessage.getTitle());
- String szRemindMSG = notificationMessage.getRemindMSG();
- //LogUtils.d(TAG, "szRemindMSG : " + szRemindMSG);
- //mrvRemindNotificationView.setTextViewText(R.id.remoteviewTextView2, szRemindMSG);
- if (szRemindMSG != null) {
- if (szRemindMSG.trim().equals("-")) {
- //LogUtils.d(TAG, "-");
- mrvRemindNotificationView.setViewVisibility(R.id.remoteviewCharge, View.GONE);
- mrvRemindNotificationView.setViewVisibility(R.id.remoteviewUsege, View.VISIBLE);
- } else if (szRemindMSG.trim().equals("+")) {
- //LogUtils.d(TAG, "+");
- mrvRemindNotificationView.setViewVisibility(R.id.remoteviewUsege, View.GONE);
- mrvRemindNotificationView.setViewVisibility(R.id.remoteviewCharge, View.VISIBLE);
- }
- mrvRemindNotificationView.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
- //给我remoteViews上的控件tv_content添加监听事件
- //remoteViews.setOnClickPendingIntent(R.id.remoteviewLinearLayout1, pi);
- //return mrvServiceNotificationView;
- }
- }
-
- void initmrvServiceNotificationView(ControlCenterService service, NotificationMessage notificationMessage) {
- mrvServiceNotificationView = new RemoteViews(service.getPackageName(), R.layout.view_servicenotification);
- mrvServiceNotificationView.setTextViewText(R.id.remoteviewTextView1, notificationMessage.getTitle());
- //String szRemindMSG = notificationMessage.getRemindMSG();
- //mrvServiceNotificationView.setTextViewText(R.id.remoteviewTextView2, szRemindMSG);
- //rvServiceNotificationView.setTextViewText(R.id.remoteviewTextView3, notificationMessage.getContent() + Integer.toString(nTest));
- mrvServiceNotificationView.setTextViewText(R.id.remoteviewTextView3, notificationMessage.getContent());
- mrvServiceNotificationView.setImageViewResource(R.id.remoteviewImageView1, R.drawable.ic_launcher);
- //给我remoteViews上的控件tv_content添加监听事件
- //remoteViews.setOnClickPendingIntent(R.id.remoteviewLinearLayout1, pi);
- //return mrvServiceNotificationView;
- }
-
- void setForegroundNotificationRemoteViews(ControlCenterService service, NotificationMessage notificationMessage) {
- initmrvServiceNotificationView(service, notificationMessage);
- mForegroundNotification.contentView = mrvServiceNotificationView;
- mForegroundNotification.bigContentView = mrvServiceNotificationView;
- }
-
- void setRemindNotificationRemoteViews(ControlCenterService service, NotificationMessage notificationMessage) {
- initmrvRemindNotificationView(service, notificationMessage);
- mRemindNotification.contentView = mrvRemindNotificationView;
- mRemindNotification.bigContentView = mrvRemindNotificationView;
- }
-
- // 更新服务通知
- //
- public void updateForegroundNotification(ControlCenterService service, NotificationMessage notificationMessage) {
- setForegroundNotificationRemoteViews(service, notificationMessage);
- mNotificationManager.notify(_mnServiceNotificationID, mForegroundNotification);
-
- }
-
- // 创建并发送电量提醒通知
- //
- public void updateRemindNotification(ControlCenterService service, NotificationMessage notificationMessage) {
- //LogUtils.d(TAG, "updateRemindNotification : " + notificationMessage.getRemindMSG());
- setRemindNotificationRemoteViews(service, notificationMessage);
- mNotificationManager.notify(_mnRemindNotificationID, mRemindNotification);
- }
-
- public void createRemindNotification(ControlCenterService service, NotificationMessage notificationMessage) {
- //LogUtils.d(TAG, "notificationMessage : " + notificationMessage.getRemindMSG());
- //创建Notification,传入Context和channelId
- Intent intent = new Intent();//这个intent会传给目标,可以使用getIntent来获取
- intent.setPackage(service.getPackageName());
- intent.setClass(service, MainActivity.class);
- //这里放一个count用来区分每一个通知
- //intent.putExtra("intent", "intent--->" + count);//这里设置一个数据,带过去
-
- //参数1:context 上下文对象
- //参数2:发送者私有的请求码(Private request code for the sender)
- //参数3:intent 意图对象
- //参数4:必须为FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT,FLAG_UPDATE_CURRENT,中的一个
- //mRemindPendingIntent = PendingIntent.getActivity(mService, 1, intent, PendingIntent.FLAG_CANCEL_CURRENT);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- mRemindPendingIntent = PendingIntent.getActivity(service,
- 1, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- } else {
- mRemindPendingIntent = PendingIntent.getActivity(service,
- 1, intent, PendingIntent.FLAG_IMMUTABLE);
- }
-
- mRemindNotification = new Notification.Builder(service, _mszChannelIDRemind)
- .setAutoCancel(true)
- .setContentTitle(notificationMessage.getTitle())
- .setContentText(notificationMessage.getContent())
- .setWhen(System.currentTimeMillis())
- .setSmallIcon(R.drawable.ic_launcher)
- //设置红色
- .setColor(Color.parseColor("#F00606"))
- .setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.ic_launcher))
- .setContentIntent(mRemindPendingIntent)
- .build();
- setRemindNotificationRemoteViews(service, notificationMessage);
- }
-
- public static void cancelRemindNotification(Context context){
- // 获取 NotificationManager 实例
- NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- // 撤回指定 ID 的通知栏消息
- notificationManager.cancel(_mnRemindNotificationID);
- }
-}
-
-