修复应用初始安装时电量提醒无声音的问题。
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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-30,Java7兼容,前台服务无铃声,提醒通知用系统默认铃声
|
||||||
*/
|
*/
|
||||||
public class NotificationManagerUtils {
|
public class NotificationManagerUtils {
|
||||||
// ================================== 静态常量(置顶统一管理,杜绝魔法值)=================================
|
// ================================== 静态常量(置顶统一管理,杜绝魔法值)=================================
|
||||||
public static final String TAG = "NotificationManagerUtils";
|
public static final String TAG = "NotificationManagerUtils";
|
||||||
// 通知渠道ID(区分前台服务/提醒通知,适配API26+)
|
// 通知渠道ID(API26+ 必需,区分通知类型)
|
||||||
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)=================================
|
// ================================== 内部辅助方法(创建跳转PendingIntent,API30安全适配)=================================
|
||||||
/**
|
/**
|
||||||
* 创建跳转主页面的PendingIntent(适配API30+安全要求,添加IMMUTABLE标记)
|
* 创建跳转MainActivity的PendingIntent,API23+ 添加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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user