修复应用初始安装时电量提醒无声音的问题。

This commit is contained in:
2025-12-17 15:17:45 +08:00
parent 1e9a6adc88
commit 683dc6791e
3 changed files with 143 additions and 195 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle #Created by .winboll/winboll_app_build.gradle
#Wed Dec 17 05:49:07 GMT 2025 #Wed Dec 17 07:16:02 GMT 2025
stageCount=10 stageCount=10
libraryProject= libraryProject=
baseVersion=15.14 baseVersion=15.14
publishVersion=15.14.9 publishVersion=15.14.9
buildCount=2 buildCount=11
baseBetaVersion=15.14.10 baseBetaVersion=15.14.10

View File

@@ -11,7 +11,9 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.media.RingtoneManager;
import android.os.Build; import android.os.Build;
import android.provider.Settings;
import cc.winboll.studio.libappbase.LogUtils; import cc.winboll.studio.libappbase.LogUtils;
import cc.winboll.studio.powerbell.MainActivity; import cc.winboll.studio.powerbell.MainActivity;
import cc.winboll.studio.powerbell.R; import cc.winboll.studio.powerbell.R;
@@ -20,198 +22,193 @@ import cc.winboll.studio.powerbell.models.NotificationMessage;
/** /**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com> * @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/17 13:42 * @Date 2025/12/17 13:42
* @Describe 通知工具类:统一管理前台服务通知、电池提醒通知适配API19-30强化兼容性与容错性(取消所有振动) * @Describe 通知工具类:统一管理前台服务/电池提醒通知适配API19-30Java7兼容前台服务无铃声提醒通知用系统默认铃声
*/ */
public class NotificationManagerUtils { public class NotificationManagerUtils {
// ================================== 静态常量(置顶统一管理,杜绝魔法值)================================= // ================================== 静态常量(置顶统一管理,杜绝魔法值)=================================
public static final String TAG = "NotificationManagerUtils"; public static final String TAG = "NotificationManagerUtils";
// 通知渠道ID区分前台服务/提醒通知适配API26+ // 通知渠道IDAPI26+ 必需,区分通知类型
public static final String CHANNEL_ID_FOREGROUND = "cc.winboll.studio.powerbell.channel.foreground"; public static final String CHANNEL_ID_FOREGROUND = "cc.winboll.studio.powerbell.channel.foreground";
public static final String CHANNEL_ID_REMIND = "cc.winboll.studio.powerbell.channel.remind"; public static final String CHANNEL_ID_REMIND = "cc.winboll.studio.powerbell.channel.remind";
// 通知ID唯一标识避免重复/混淆 // 通知ID唯一标识避免重复
public static final int NOTIFY_ID_FOREGROUND_SERVICE = 1001; public static final int NOTIFY_ID_FOREGROUND_SERVICE = 1001;
public static final int NOTIFY_ID_REMIND = 1002; public static final int NOTIFY_ID_REMIND = 1002;
// 低版本兼容配置API<21 通知图标默认值,避免显示异常) // 低版本兼容:默认通知图标API<21 避免显示异常)
private static final int NOTIFICATION_DEFAULT_ICON = R.drawable.ic_launcher; private static final int NOTIFICATION_DEFAULT_ICON = R.drawable.ic_launcher;
// ================================== 成员变量(按功能分类,封装保护,避免外部篡改================================= // ================================== 成员变量(私有封装,按依赖优先级排序=================================
// 核心依赖资源 // 核心上下文(应用级,避免内存泄漏)
private Context mContext; private Context mContext;
// 系统通知服务(核心依赖)
private NotificationManager mNotificationManager; private NotificationManager mNotificationManager;
// 前台服务通知实例(单独持有,便于更新/取消) // 前台服务通知实例(单独持有,便于更新/取消)
private Notification mForegroundServiceNotify; private Notification mForegroundServiceNotify;
// ================================== 构造方法(初始化核心资源,前置校验防崩溃================================= // ================================== 构造方法(初始化核心资源,前置校验)=================================
public NotificationManagerUtils(Context context) { public NotificationManagerUtils(Context context) {
LogUtils.d(TAG, "初始化通知工具类"); LogUtils.d(TAG, "NotificationManagerUtils init start");
// 前置校验Context非空,避免后续空指针 // 前置校验Context非空
if (context == null) { if (context == null) {
LogUtils.e(TAG, "初始化失败Context为空"); LogUtils.e(TAG, "init failed: context is null");
return; return;
} }
// 持有应用上下文,杜绝内存泄漏 // 初始化核心资源
this.mContext = context.getApplicationContext(); this.mContext = context.getApplicationContext();
// 获取系统通知服务,初始化核心依赖
this.mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); this.mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
// 初始化通知渠道API26+ 必需) // 初始化通知渠道API26+ 必需)
initNotificationChannels(); initNotificationChannels();
LogUtils.d(TAG, "通知工具类初始化完成"); LogUtils.d(TAG, "NotificationManagerUtils init success");
} }
// ================================== 核心初始化(通知渠道,适配API26+,取消振动================================= // ================================== 核心初始化方法通知渠道API分级适配=================================
/** /**
* 初始化通知渠道:前台服务渠道(低打扰)、电池提醒渠道(正常感知),均关闭振动 * 初始化通知渠道:前台服务渠道(无铃声+无振动)、提醒渠道(系统默认铃声+无振动
*/ */
private void initNotificationChannels() { private void initNotificationChannels() {
LogUtils.d(TAG, "开始初始化通知渠道"); // API<26 无渠道机制,直接返回
// API<26 无渠道机制,直接跳过;通知服务为空也不执行 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || mNotificationManager == null) { LogUtils.d(TAG, "initNotificationChannels: API<26, no need channel");
LogUtils.d(TAG, "无需初始化渠道API<26 或 NotificationManager为空"); return;
}
// 通知服务为空,避免空指针
if (mNotificationManager == null) {
LogUtils.e(TAG, "initNotificationChannels failed: NotificationManager is null");
return; return;
} }
// 1. 前台服务渠道(低优先级,后台运行无打扰,关闭振动 // 1. 前台服务渠道(低优先级,后台保活无打扰)
NotificationChannel foregroundChannel = new NotificationChannel( NotificationChannel foregroundChannel = new NotificationChannel(
CHANNEL_ID_FOREGROUND, CHANNEL_ID_FOREGROUND,
"电池服务保活", "电池服务保活",
NotificationManager.IMPORTANCE_LOW NotificationManager.IMPORTANCE_LOW
); );
foregroundChannel.setDescription("电池监测服务后台稳定运行,无弹窗、无震动、无声音"); foregroundChannel.setDescription("电池监测服务后台运行,无声音、无振动");
foregroundChannel.enableLights(false); foregroundChannel.enableLights(false);
foregroundChannel.enableVibration(false); // 关闭振动 foregroundChannel.enableVibration(false);
foregroundChannel.setSound(null, null); foregroundChannel.setSound(null, null); // 强制无铃声
foregroundChannel.setShowBadge(false); foregroundChannel.setShowBadge(false);
foregroundChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); foregroundChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
// 2. 电池提醒渠道(中优先级,确保用户感知,关闭振动) // 2. 电池提醒渠道(中优先级,系统默认铃声,无振动)
NotificationChannel remindChannel = new NotificationChannel( NotificationChannel remindChannel = new NotificationChannel(
CHANNEL_ID_REMIND, CHANNEL_ID_REMIND,
"电池状态提醒", "电池状态提醒",
NotificationManager.IMPORTANCE_DEFAULT NotificationManager.IMPORTANCE_DEFAULT
); );
remindChannel.setDescription("电池满电/低电量提醒,及时保护电池健康(无振动"); remindChannel.setDescription("电池满电/低电量提醒,系统默认铃声,无振动");
remindChannel.enableLights(true); remindChannel.enableLights(true);
remindChannel.enableVibration(false); // 关闭振动(删除振动模式配置) remindChannel.enableVibration(false);
remindChannel.setSound(null, null); remindChannel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), Notification.AUDIO_ATTRIBUTES_DEFAULT);
remindChannel.setShowBadge(false); remindChannel.setShowBadge(false);
remindChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); remindChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
// 注册渠道到系统 // 注册渠道到系统
mNotificationManager.createNotificationChannel(foregroundChannel); mNotificationManager.createNotificationChannel(foregroundChannel);
mNotificationManager.createNotificationChannel(remindChannel); mNotificationManager.createNotificationChannel(remindChannel);
LogUtils.d(TAG, "通知渠道初始化完成(前台+提醒各1个均关闭振动"); LogUtils.d(TAG, "initNotificationChannels success: foreground+remind channel created");
} }
// ================================== 对外核心方法(前台服务通知:启动+更新+取消)================================= // ================================== 对外核心方法(前台服务通知:启动/更新/取消)=================================
/** /**
* 启动前台服务通知(适配API19-30无FOREGROUND_SERVICE_TYPE全版本通用 * 启动前台服务通知API30适配无铃声
*/ */
public void startForegroundServiceNotify(Service service, NotificationMessage message) { public void startForegroundServiceNotify(Service service, NotificationMessage message) {
LogUtils.d(TAG, "启动前台服务通知通知ID" + NOTIFY_ID_FOREGROUND_SERVICE); LogUtils.d(TAG, "startForegroundServiceNotify start, notifyId: " + NOTIFY_ID_FOREGROUND_SERVICE);
// 前置校验:依赖参数非空,避免崩溃 // 前置校验:参数非空
if (service == null || message == null || mNotificationManager == null) { if (service == null || message == null || mNotificationManager == null) {
LogUtils.e(TAG, "启动失败:Service/消息/NotificationManager为空"); LogUtils.e(TAG, "startForegroundServiceNotify failed: param is null");
return; return;
} }
// 构建前台通知实例 // 构建前台通知
mForegroundServiceNotify = buildForegroundNotification(message); mForegroundServiceNotify = buildForegroundNotification(message);
if (mForegroundServiceNotify == null) { if (mForegroundServiceNotify == null) {
LogUtils.e(TAG, "启动失败:前台通知构建失败"); LogUtils.e(TAG, "startForegroundServiceNotify failed: build notify null");
return; return;
} }
// 启动前台服务(全版本通用写法适配API30无类型限制 // 启动前台服务(API30无FOREGROUND_SERVICE_TYPE限制全版本通用)
try { try {
service.startForeground(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify); service.startForeground(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
LogUtils.d(TAG, "前台服务通知启动成功"); LogUtils.d(TAG, "startForegroundServiceNotify success");
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "启动异常可能是5秒内未调用/权限缺失)", e); LogUtils.e(TAG, "startForegroundServiceNotify exception", e);
} }
} }
/** /**
* 更新前台服务通知内容复用通知ID实时刷新显示 * 更新前台服务通知内容复用通知ID保持无铃声
*/ */
public void updateForegroundServiceNotify(NotificationMessage message) { public void updateForegroundServiceNotify(NotificationMessage message) {
LogUtils.d(TAG, "更新前台服务通知通知ID" + NOTIFY_ID_FOREGROUND_SERVICE); LogUtils.d(TAG, "updateForegroundServiceNotify start, notifyId: " + NOTIFY_ID_FOREGROUND_SERVICE);
if (message == null || mNotificationManager == null) { if (message == null || mNotificationManager == null) {
LogUtils.e(TAG, "更新失败:消息/NotificationManager为空"); LogUtils.e(TAG, "updateForegroundServiceNotify failed: param is null");
return; return;
} }
// 重新构建通知,覆盖旧实例
mForegroundServiceNotify = buildForegroundNotification(message); mForegroundServiceNotify = buildForegroundNotification(message);
if (mForegroundServiceNotify == null) { if (mForegroundServiceNotify == null) {
LogUtils.e(TAG, "更新失败:前台通知构建失败"); LogUtils.e(TAG, "updateForegroundServiceNotify failed: build notify null");
return; return;
} }
// 刷新通知显示
try { try {
mNotificationManager.notify(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify); mNotificationManager.notify(NOTIFY_ID_FOREGROUND_SERVICE, mForegroundServiceNotify);
LogUtils.d(TAG, "前台服务通知更新成功"); LogUtils.d(TAG, "updateForegroundServiceNotify success");
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "更新异常", e); LogUtils.e(TAG, "updateForegroundServiceNotify exception", e);
} }
} }
/** /**
* 取消前台服务通知(销毁Service时调用,避免通知残留 * 取消前台服务通知Service销毁时调用)
*/ */
public void cancelForegroundServiceNotify() { public void cancelForegroundServiceNotify() {
LogUtils.d(TAG, "取消前台服务通知通知ID" + NOTIFY_ID_FOREGROUND_SERVICE); LogUtils.d(TAG, "cancelForegroundServiceNotify start, notifyId: " + NOTIFY_ID_FOREGROUND_SERVICE);
// 先取消系统通知
cancelNotification(NOTIFY_ID_FOREGROUND_SERVICE); cancelNotification(NOTIFY_ID_FOREGROUND_SERVICE);
// 置空实例加速GC回收 mForegroundServiceNotify = null; // 置空释放
mForegroundServiceNotify = null; LogUtils.d(TAG, "cancelForegroundServiceNotify success");
LogUtils.d(TAG, "前台服务通知取消完成,实例已置空");
} }
// ================================== 对外核心方法(电池提醒通知:发送)================================= // ================================== 对外核心方法(电池提醒通知:发送)=================================
/** /**
* 发送电池提醒通知(满电/低电量,适配全版本提醒效果,无振动) * 发送电池提醒通知(系统默认铃声,无振动)
*/ */
public void showRemindNotification(Context context, NotificationMessage message) { public void showRemindNotification(Context context, NotificationMessage message) {
LogUtils.d(TAG, "发送电池提醒通知,标题:" + (message.getTitle() != null ? message.getTitle() : "默认提醒")); LogUtils.d(TAG, "showRemindNotification start, notifyId: " + NOTIFY_ID_REMIND);
// 前置校验:依赖参数非空
if (context == null || message == null || mNotificationManager == null) { if (context == null || message == null || mNotificationManager == null) {
LogUtils.e(TAG, "发送失败Context/消息/NotificationManager为空"); LogUtils.e(TAG, "showRemindNotification failed: param is null");
return; return;
} }
// 构建提醒通知实例
Notification remindNotify = buildRemindNotification(context, message); Notification remindNotify = buildRemindNotification(context, message);
if (remindNotify == null) { if (remindNotify == null) {
LogUtils.e(TAG, "发送失败:提醒通知构建失败"); LogUtils.e(TAG, "showRemindNotification failed: build notify null");
return; return;
} }
// 发送通知到系统
try { try {
mNotificationManager.notify(NOTIFY_ID_REMIND, remindNotify); mNotificationManager.notify(NOTIFY_ID_REMIND, remindNotify);
LogUtils.d(TAG, "电池提醒通知发送成功通知ID" + NOTIFY_ID_REMIND); LogUtils.d(TAG, "showRemindNotification success");
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "发送异常", e); LogUtils.e(TAG, "showRemindNotification exception", e);
} }
} }
// ================================== 对外核心方法(通知取消:单个+全部)================================= // ================================== 对外工具方法(通知取消:单个/全部)=================================
/** /**
* 取消指定ID的通知 * 取消指定ID的通知
*/ */
public void cancelNotification(int notifyId) { public void cancelNotification(int notifyId) {
LogUtils.d(TAG, "取消指定通知通知ID" + notifyId);
if (mNotificationManager == null) { if (mNotificationManager == null) {
LogUtils.e(TAG, "取消失败:NotificationManager为空"); LogUtils.e(TAG, "cancelNotification failed: NotificationManager is null");
return; return;
} }
try { try {
mNotificationManager.cancel(notifyId); mNotificationManager.cancel(notifyId);
LogUtils.d(TAG, "通知取消成功"); LogUtils.d(TAG, "cancelNotification success, notifyId: " + notifyId);
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "取消异常", e); LogUtils.e(TAG, "cancelNotification exception, notifyId: " + notifyId, e);
} }
} }
@@ -219,192 +216,157 @@ public class NotificationManagerUtils {
* 取消所有通知(兜底场景使用) * 取消所有通知(兜底场景使用)
*/ */
public void cancelAllNotifications() { public void cancelAllNotifications() {
LogUtils.d(TAG, "取消所有通知");
if (mNotificationManager == null) { if (mNotificationManager == null) {
LogUtils.e(TAG, "取消失败:NotificationManager为空"); LogUtils.e(TAG, "cancelAllNotifications failed: NotificationManager is null");
return; return;
} }
try { try {
mNotificationManager.cancelAll(); mNotificationManager.cancelAll();
LogUtils.d(TAG, "所有通知取消成功"); LogUtils.d(TAG, "cancelAllNotifications success");
} catch (Exception e) { } catch (Exception e) {
LogUtils.e(TAG, "取消异常", e); LogUtils.e(TAG, "cancelAllNotifications exception", e);
} }
} }
// ================================== 内部辅助方法(通知构建:前台服务通知)================================= // ================================== 内部辅助方法(通知构建:前台服务通知)=================================
/** /**
* 构建前台服务通知(API分级适配低版本无打扰高版本渠道管控无振动) * 构建前台服务通知(全版本无铃声+无振动)
*/ */
private Notification buildForegroundNotification(NotificationMessage message) { private Notification buildForegroundNotification(NotificationMessage message) {
if (message == null || mContext == null) { if (message == null || mContext == null) {
LogUtils.e(TAG, "前台通知构建失败:参数/Context为空"); LogUtils.e(TAG, "buildForegroundNotification failed: param is null");
return null; return null;
} }
// 内容兜底:避免消息标题/内容为空 // 内容兜底
String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : "电池服务运行中"; String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : "电池服务运行中";
String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : "后台持续监测电池状态,保护电池健康"; String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : "后台监测电池状态";
Notification.Builder builder; Notification.Builder builder;
// API分级构建,确保全版本兼容 // API分级构建
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// API26+:绑定前台服务渠道,低优先级无打扰(渠道已关闭振动 // API26+:绑定前台渠道(渠道已配置无铃声
builder = new Notification.Builder(mContext, CHANNEL_ID_FOREGROUND) builder = new Notification.Builder(mContext, CHANNEL_ID_FOREGROUND);
.setSmallIcon(NOTIFICATION_DEFAULT_ICON)
.setContentTitle(title)
.setContentText(content)
.setAutoCancel(false)
.setOngoing(true) // 前台服务通知不可手动关闭
.setWhen(System.currentTimeMillis())
.setTicker(title);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// API21-25添加大图标+主题色,手动关闭声音震动
builder = new Notification.Builder(mContext)
.setSmallIcon(NOTIFICATION_DEFAULT_ICON)
.setLargeIcon(getAppIcon(mContext))
.setColor(mContext.getResources().getColor(R.color.colorPrimary))
.setContentTitle(title)
.setContentText(content)
.setAutoCancel(false)
.setOngoing(true)
.setWhen(System.currentTimeMillis())
.setTicker(title)
.setPriority(Notification.PRIORITY_LOW); // 低优先级,减少打扰
} else { } else {
// API<21基础配置适配旧机型 // API<26直接构建手动禁用铃声振动
builder = new Notification.Builder(mContext) builder = new Notification.Builder(mContext);
.setSmallIcon(NOTIFICATION_DEFAULT_ICON) builder.setSound(null);
.setContentTitle(title) builder.setVibrate(new long[]{0});
.setContentText(content) builder.setDefaults(0);
.setAutoCancel(false) }
.setOngoing(true)
.setWhen(System.currentTimeMillis()) // 通用配置
.setTicker(title) builder.setSmallIcon(NOTIFICATION_DEFAULT_ICON)
.setContentTitle(title)
.setContentText(content)
.setAutoCancel(false)
.setOngoing(true) // 不可手动关闭
.setWhen(System.currentTimeMillis())
.setContentIntent(createJumpPendingIntent(mContext, 0));
// API21+ 新增大图标+主题色
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder.setLargeIcon(getAppIcon(mContext))
.setColor(mContext.getResources().getColor(R.color.colorPrimary))
.setPriority(Notification.PRIORITY_LOW); .setPriority(Notification.PRIORITY_LOW);
} }
// 绑定跳转意图:点击通知打开主页面 return builder.build();
builder.setContentIntent(createJumpPendingIntent(mContext, 0));
Notification notification = builder.build();
// API<26无渠道手动屏蔽声音/震动,确保无打扰(彻底关闭振动)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
notification.defaults = 0;
notification.vibrate = new long[]{0}; // 振动时长设为0彻底关闭
notification.sound = null;
}
return notification;
} }
// ================================== 内部辅助方法(通知构建:电池提醒通知)================================= // ================================== 内部辅助方法(通知构建:电池提醒通知)=================================
/** /**
* 构建电池提醒通知(API分级适配确保提醒效果无振动支持手动关闭 * 构建电池提醒通知(全版本系统默认铃声+无振动
*/ */
private Notification buildRemindNotification(Context context, NotificationMessage message) { private Notification buildRemindNotification(Context context, NotificationMessage message) {
if (context == null || message == null) { if (context == null || message == null) {
LogUtils.e(TAG, "提醒通知构建失败:参数/Context为空"); LogUtils.e(TAG, "buildRemindNotification failed: param is null");
return null; return null;
} }
// 内容兜底:避免消息标题/内容为空 // 内容兜底
String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : "电池状态提醒"; String title = message.getTitle() != null && !message.getTitle().isEmpty() ? message.getTitle() : "电池状态提醒";
String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : "电池状态异常,请及时处理"; String content = message.getContent() != null && !message.getContent().isEmpty() ? message.getContent() : "电池状态异常,请及时处理";
Notification.Builder builder; Notification.Builder builder;
// API分级构建,确保提醒效果一致(全版本关闭振动) // API分级构建
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// API26+:绑定提醒渠道渠道已关闭振动,无需额外配置 // API26+:绑定提醒渠道渠道已配置默认铃声)
builder = new Notification.Builder(context, CHANNEL_ID_REMIND) builder = new Notification.Builder(context, CHANNEL_ID_REMIND);
.setSmallIcon(NOTIFICATION_DEFAULT_ICON)
.setContentTitle(title)
.setContentText(content)
.setAutoCancel(true) // 点击后自动关闭
.setOngoing(false)
.setWhen(System.currentTimeMillis())
.setTicker(title);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// API21-25添加大图标+主题色关闭振动删除setVibrationPattern和振动默认值
builder = new Notification.Builder(context)
.setSmallIcon(NOTIFICATION_DEFAULT_ICON)
.setLargeIcon(getAppIcon(context))
.setColor(context.getResources().getColor(R.color.colorPrimary))
.setContentTitle(title)
.setContentText(content)
.setAutoCancel(true)
.setOngoing(false)
.setWhen(System.currentTimeMillis())
.setTicker(title)
.setPriority(Notification.PRIORITY_DEFAULT) // 中优先级,确保感知
.setDefaults(Notification.DEFAULT_LIGHTS); // 仅保留灯光提醒,关闭振动
} else { } else {
// API<21基础配置仅保留灯光,关闭振动 // API<26手动配置默认铃声,关闭振动
builder = new Notification.Builder(context) builder = new Notification.Builder(context);
.setSmallIcon(NOTIFICATION_DEFAULT_ICON) builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI) // 显式默认铃声
.setContentTitle(title) .setVibrate(new long[]{0})
.setContentText(content) .setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_SOUND);
.setAutoCancel(true) }
.setOngoing(false)
.setWhen(System.currentTimeMillis()) // 通用配置
.setTicker(title) builder.setSmallIcon(NOTIFICATION_DEFAULT_ICON)
.setPriority(Notification.PRIORITY_DEFAULT) .setContentTitle(title)
.setDefaults(Notification.DEFAULT_LIGHTS); // 仅保留灯光,关闭振动 .setContentText(content)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), Notification.AUDIO_ATTRIBUTES_DEFAULT)
.setAutoCancel(true) // 点击关闭
.setOngoing(false)
.setWhen(System.currentTimeMillis())
.setContentIntent(createJumpPendingIntent(context, 1));
// API21+ 新增大图标+主题色
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder.setLargeIcon(getAppIcon(context))
.setColor(context.getResources().getColor(R.color.colorPrimary))
.setPriority(Notification.PRIORITY_DEFAULT);
} }
// 绑定跳转意图:点击通知打开主页面
builder.setContentIntent(createJumpPendingIntent(context, 1));
return builder.build(); return builder.build();
} }
// ================================== 内部辅助方法(通用:创建跳转PendingIntent================================= // ================================== 内部辅助方法创建跳转PendingIntentAPI30安全适配=================================
/** /**
* 创建跳转主页面的PendingIntent适配API30+安全要求,添加IMMUTABLE标记 * 创建跳转MainActivity的PendingIntentAPI23+ 添加IMMUTABLE标记(避免安全异常
*/ */
private PendingIntent createJumpPendingIntent(Context context, int requestCode) { private PendingIntent createJumpPendingIntent(Context context, int requestCode) {
// 跳转意图打开MainActivity清除栈顶重复页面 Intent intent = new Intent(context, MainActivity.class);
Intent jumpIntent = new Intent(context, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
jumpIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
// 适配API30+安全要求:API23+ 必添加FLAG_IMMUTABLE避免安全异常 // API23+ 必添加IMMUTABLE适配API30安全规范
int flags = PendingIntent.FLAG_UPDATE_CURRENT; int flags = PendingIntent.FLAG_UPDATE_CURRENT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
flags |= PendingIntent.FLAG_IMMUTABLE; flags |= PendingIntent.FLAG_IMMUTABLE;
} }
return PendingIntent.getActivity(context, requestCode, jumpIntent, flags); return PendingIntent.getActivity(context, requestCode, intent, flags);
} }
// ================================== 内部辅助方法(通用:获取APP图标================================= // ================================== 内部辅助方法获取APP图标,异常兜底=================================
/** /**
* 获取APP图标API21+ 大图标显示使用,失败返回默认图标 * 获取APP图标失败返回默认图标
*/ */
private Bitmap getAppIcon(Context context) { private Bitmap getAppIcon(Context context) {
try { try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); PackageInfo pkgInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
return BitmapFactory.decodeResource(context.getResources(), packageInfo.applicationInfo.icon); return BitmapFactory.decodeResource(context.getResources(), pkgInfo.applicationInfo.icon);
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
LogUtils.e(TAG, "获取APP图标失败", e); LogUtils.e(TAG, "getAppIcon exception", e);
// 异常兜底:返回默认图标,避免显示空白
return BitmapFactory.decodeResource(context.getResources(), NOTIFICATION_DEFAULT_ICON); return BitmapFactory.decodeResource(context.getResources(), NOTIFICATION_DEFAULT_ICON);
} }
} }
// ================================== 对外工具方法(获取前台通知实例,仅可读)================================= // ================================== 对外 getter 方法(前台通知实例,读)=================================
public Notification getForegroundServiceNotify() { public Notification getForegroundServiceNotify() {
return mForegroundServiceNotify; return mForegroundServiceNotify;
} }
// ================================== 资源释放(彻底释放依赖,避免内存泄漏)================================= // ================================== 资源释放方法(避免内存泄漏)=================================
/**
* 释放资源,销毁时调用
*/
public void release() { public void release() {
LogUtils.d(TAG, "开始释放通知工具类资源"); LogUtils.d(TAG, "release start");
// 释放前先取消前台通知(兜底,避免残留)
cancelForegroundServiceNotify(); cancelForegroundServiceNotify();
// 置空核心依赖加速GC回收
mNotificationManager = null; mNotificationManager = null;
mContext = null; mContext = null;
LogUtils.d(TAG, "通知工具类资源释放完成"); LogUtils.d(TAG, "release success");
} }
} }

View File

@@ -1,14 +0,0 @@
package cc.winboll.studio.powerbell.utils;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/12/17 13:42
* @Describe NotificationManagerUtils
*/
public class NotificationManagerUtils6 {
public static final String TAG = "NotificationManagerUtils6";
}