添加任务铃声循环播放功能。
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#Created by .winboll/winboll_app_build.gradle
|
||||||
#Fri Oct 03 11:38:10 HKT 2025
|
#Fri Oct 03 15:08:05 GMT 2025
|
||||||
stageCount=9
|
stageCount=9
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.0
|
baseVersion=15.0
|
||||||
publishVersion=15.0.8
|
publishVersion=15.0.8
|
||||||
buildCount=0
|
buildCount=1
|
||||||
baseBetaVersion=15.0.9
|
baseBetaVersion=15.0.9
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package cc.winboll.studio.positions.utils;
|
|||||||
/**
|
/**
|
||||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||||
* @Date 2025/09/30 16:09
|
* @Date 2025/09/30 16:09
|
||||||
* @Describe NotificationUtils
|
* @Describe NotificationUtils(适配API 30,修复系统默认铃声获取,任务通知循环响铃)
|
||||||
*/
|
*/
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
@@ -11,175 +11,183 @@ import android.app.NotificationManager;
|
|||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.media.RingtoneManager; // 导入RingtoneManager(关键:用于获取系统默认铃声)
|
||||||
|
import android.net.Uri; // 导入Uri(存储铃声路径)
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import cc.winboll.studio.positions.R;
|
import cc.winboll.studio.positions.R;
|
||||||
import cc.winboll.studio.positions.activities.LocationActivity; // 引入你的前台服务类
|
import cc.winboll.studio.positions.activities.LocationActivity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通知栏工具类:专注于任务相关通知的显示与点击跳转 + 前台服务通知管理
|
* 通知栏工具类:
|
||||||
* 核心功能:
|
* 1. 任务通知:铃声循环播放(适配API 30),修复系统默认铃声获取方式
|
||||||
* 1. 显示任务描述通知,点击后携带positionId/taskId跳转到LocationActivity
|
* 2. 前台服务通知:低打扰(无声无震动),符合API 30规范
|
||||||
* 2. 创建前台服务通知(用于DistanceRefreshService保活,符合系统前台服务规范)
|
|
||||||
*/
|
*/
|
||||||
public class NotificationUtil {
|
public class NotificationUtil {
|
||||||
public static final String TAG = "NotificationUtils";
|
public static final String TAG = "NotificationUtils";
|
||||||
// 1. 任务通知相关常量(原有)
|
// 任务通知常量(独立渠道,确保循环铃声配置不冲突)
|
||||||
private static final String TASK_NOTIFICATION_CHANNEL_ID = "task_notification_channel_01";
|
private static final String TASK_NOTIFICATION_CHANNEL_ID = "task_notification_channel_01";
|
||||||
private static final String TASK_NOTIFICATION_CHANNEL_NAME = "任务通知";
|
private static final String TASK_NOTIFICATION_CHANNEL_NAME = "任务通知(循环铃声)";
|
||||||
// 2. 前台服务通知新增常量(独立渠道,避免与普通任务通知混淆)
|
// 前台服务通知常量(独立渠道,低打扰)
|
||||||
private static final String FOREGROUND_SERVICE_CHANNEL_ID = "foreground_location_service_channel_02";
|
private static final String FOREGROUND_SERVICE_CHANNEL_ID = "foreground_location_service_channel_02";
|
||||||
private static final String FOREGROUND_SERVICE_CHANNEL_NAME = "位置服务";
|
private static final String FOREGROUND_SERVICE_CHANNEL_NAME = "位置服务";
|
||||||
public static final int FOREGROUND_SERVICE_NOTIFICATION_ID = 10086; // 固定ID(前台服务通知无需动态生成)
|
public static final int FOREGROUND_SERVICE_NOTIFICATION_ID = 10086; // 固定前台服务通知ID
|
||||||
|
|
||||||
// ---------------------- 原有功能:任务通知(不变) ----------------------
|
// ---------------------- 核心:任务通知(循环响铃+修复系统默认铃声) ----------------------
|
||||||
private static int getNotificationId(String taskId) {
|
private static int getNotificationId(String taskId) {
|
||||||
return taskId.hashCode() & 0xFFFFFF;
|
return taskId.hashCode() & 0xFFFFFF; // 确保通知ID唯一且非负
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void show(Context context, String taskId, String positionId, String taskDescription) {
|
public static void show(Context context, String taskId, String positionId, String taskDescription) {
|
||||||
if (context == null || taskId == null || positionId == null) {
|
if (context == null || taskId == null || positionId == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationManager notificationManager =
|
NotificationManager notificationManager =
|
||||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
if (notificationManager == null) {
|
if (notificationManager == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
createNotificationChannel(notificationManager);
|
// 1. 初始化通知渠道(配置循环铃声参数,使用修复后的铃声获取方式)
|
||||||
|
createNotificationChannel(notificationManager);
|
||||||
|
|
||||||
Intent jumpIntent = new Intent(context, LocationActivity.class);
|
// 2. 点击跳转Intent(携带任务/位置参数,适配API 30页面栈)
|
||||||
jumpIntent.putExtra("EXTRA_POSITION_ID", positionId);
|
Intent jumpIntent = new Intent(context, LocationActivity.class);
|
||||||
jumpIntent.putExtra("EXTRA_TASK_ID", taskId);
|
jumpIntent.putExtra("EXTRA_POSITION_ID", positionId);
|
||||||
jumpIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
jumpIntent.putExtra("EXTRA_TASK_ID", taskId);
|
||||||
|
jumpIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||||
|
|
||||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
// 3. PendingIntent(API 30强制加IMMUTABLE,避免安全异常)
|
||||||
context,
|
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||||
getNotificationId(taskId),
|
context,
|
||||||
jumpIntent,
|
getNotificationId(taskId),
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ?
|
jumpIntent,
|
||||||
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT :
|
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
);
|
||||||
);
|
|
||||||
|
|
||||||
Notification notification = new NotificationCompat.Builder(context, TASK_NOTIFICATION_CHANNEL_ID)
|
// 4. 构建通知(核心:循环响铃+修复的系统默认铃声)
|
||||||
.setSmallIcon(R.mipmap.ic_launcher)
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, TASK_NOTIFICATION_CHANNEL_ID)
|
||||||
.setContentTitle("任务提醒")
|
.setSmallIcon(R.mipmap.ic_launcher) // API 30强制要求,否则通知不显示
|
||||||
.setContentText(taskDescription)
|
.setContentTitle("任务提醒")
|
||||||
.setContentIntent(pendingIntent)
|
.setContentText(taskDescription)
|
||||||
.setAutoCancel(true)
|
.setContentIntent(pendingIntent)
|
||||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
.setAutoCancel(true) // 点击后取消通知,停止循环响铃
|
||||||
.setDefaults(NotificationCompat.DEFAULT_SOUND)
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT) // 确保铃声能正常播放(API 30规则)
|
||||||
.build();
|
//.setVibrationPattern(new long[]{0, 300, 200, 300}) // 震动与铃声同步循环
|
||||||
|
.setOnlyAlertOnce(false); // 重复通知也触发循环提醒
|
||||||
|
|
||||||
notificationManager.notify(getNotificationId(taskId), notification);
|
// 关键修复:用RingtoneManager获取系统默认通知铃声(替代废弃的NotificationManager.getDefaultUri)
|
||||||
}
|
Uri defaultNotificationRingtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||||
|
if (defaultNotificationRingtone != null) {
|
||||||
|
builder.setSound(defaultNotificationRingtone); // 设置系统默认铃声
|
||||||
|
} else {
|
||||||
|
builder.setDefaults(NotificationCompat.DEFAULT_SOUND); // 极端情况:铃声Uri为空时,用默认提醒音兜底
|
||||||
|
}
|
||||||
|
|
||||||
private static void createNotificationChannel(NotificationManager notificationManager) {
|
// 5. 循环响铃核心:设置FLAG_INSISTENT(通知未取消则持续循环)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
Notification notification = builder.build();
|
||||||
// 原有:任务通知渠道
|
notification.flags |= Notification.FLAG_INSISTENT;
|
||||||
NotificationChannel taskChannel = new NotificationChannel(
|
|
||||||
TASK_NOTIFICATION_CHANNEL_ID,
|
|
||||||
TASK_NOTIFICATION_CHANNEL_NAME,
|
|
||||||
NotificationManager.IMPORTANCE_DEFAULT
|
|
||||||
);
|
|
||||||
taskChannel.setDescription("接收任务相关提醒,点击可查看任务详情");
|
|
||||||
taskChannel.enableVibration(true);
|
|
||||||
taskChannel.setVibrationPattern(new long[]{0, 300});
|
|
||||||
|
|
||||||
// 新增:前台服务通知渠道(重要性设为LOW,避免频繁打扰用户)
|
// 6. 显示通知(触发循环响铃)
|
||||||
NotificationChannel foregroundChannel = new NotificationChannel(
|
notificationManager.notify(getNotificationId(taskId), notification);
|
||||||
FOREGROUND_SERVICE_CHANNEL_ID,
|
}
|
||||||
FOREGROUND_SERVICE_CHANNEL_NAME,
|
|
||||||
NotificationManager.IMPORTANCE_LOW // 仅通知栏显示,无提示音/震动,符合后台服务低打扰需求
|
|
||||||
);
|
|
||||||
foregroundChannel.setDescription("位置服务运行中,用于后台持续获取GPS数据,关闭会影响定位功能"); // 明确告知用户服务作用
|
|
||||||
foregroundChannel.enableVibration(false); // 前台服务通知不震动(避免打扰)
|
|
||||||
foregroundChannel.setSound(null, null); // 关闭提示音(低打扰)
|
|
||||||
|
|
||||||
// 注册两个渠道(任务+前台服务)
|
// ---------------------- 核心:创建通知渠道(修复铃声配置,适配API 30) ----------------------
|
||||||
notificationManager.createNotificationChannel(taskChannel);
|
private static void createNotificationChannel(NotificationManager notificationManager) {
|
||||||
notificationManager.createNotificationChannel(foregroundChannel);
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
}
|
// 1. 任务通知渠道(用修复后的方式配置系统默认铃声)
|
||||||
}
|
NotificationChannel taskChannel = new NotificationChannel(
|
||||||
|
TASK_NOTIFICATION_CHANNEL_ID,
|
||||||
|
TASK_NOTIFICATION_CHANNEL_NAME,
|
||||||
|
NotificationManager.IMPORTANCE_DEFAULT // 重要性≥DEFAULT,否则铃声不响(API 30规则)
|
||||||
|
);
|
||||||
|
taskChannel.setDescription("任务提醒通知,铃声循环播放至点击取消");
|
||||||
|
taskChannel.enableVibration(true);
|
||||||
|
taskChannel.setVibrationPattern(new long[]{0, 300, 200, 300});
|
||||||
|
taskChannel.setAllowBubbles(false); // 避免气泡打断循环铃声
|
||||||
|
|
||||||
public static void cancel(Context context, String taskId) {
|
// 关键修复:渠道铃声也用RingtoneManager获取(与通知Builder保持一致,确保铃声统一)
|
||||||
if (context == null || taskId == null) {
|
Uri channelDefaultRingtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||||
return;
|
taskChannel.setSound(channelDefaultRingtone, null); // 绑定系统默认铃声到渠道
|
||||||
}
|
|
||||||
NotificationManager notificationManager =
|
|
||||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
||||||
if (notificationManager != null) {
|
|
||||||
notificationManager.cancel(getNotificationId(taskId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------- 新增核心功能:创建前台服务通知(供DistanceRefreshService调用) ----------------------
|
// 2. 前台服务渠道(低打扰,无声无震动)
|
||||||
/**
|
NotificationChannel foregroundChannel = new NotificationChannel(
|
||||||
* 创建前台服务通知(符合Android前台服务规范,用于启动/保活DistanceRefreshService)
|
FOREGROUND_SERVICE_CHANNEL_ID,
|
||||||
* @param context 服务上下文(直接传入DistanceRefreshService的this即可)
|
FOREGROUND_SERVICE_CHANNEL_NAME,
|
||||||
* @param serviceStatus 服务状态文本(如“正在后台获取GPS位置...”,动态展示服务状态)
|
NotificationManager.IMPORTANCE_LOW
|
||||||
* @return 可直接用于startForeground()的Notification对象
|
);
|
||||||
*/
|
foregroundChannel.setDescription("位置服务运行中,无声音/震动提醒");
|
||||||
public static Notification createForegroundServiceNotification(Context context, String serviceStatus) {
|
foregroundChannel.enableVibration(false);
|
||||||
// 安全校验:上下文非空(避免服务中调用时的空指针)
|
foregroundChannel.setSound(null, null); // 明确关闭铃声
|
||||||
if (context == null) {
|
foregroundChannel.setShowBadge(false);
|
||||||
throw new IllegalArgumentException("Context cannot be null for foreground service notification");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 步骤1:初始化通知管理器(复用已有逻辑,确保渠道已创建)
|
// 注册渠道(API 30覆盖旧配置,确保修复后的铃声生效)
|
||||||
NotificationManager notificationManager =
|
notificationManager.createNotificationChannel(taskChannel);
|
||||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
notificationManager.createNotificationChannel(foregroundChannel);
|
||||||
if (notificationManager != null) {
|
}
|
||||||
createNotificationChannel(notificationManager); // 确保前台服务渠道已注册
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 步骤2:构建“点击通知跳转至位置管理页”的Intent(用户点击通知可进入功能页)
|
// ---------------------- 原有功能:取消任务通知(停止循环响铃) ----------------------
|
||||||
Intent jumpIntent = new Intent(context, LocationActivity.class);
|
public static void cancel(Context context, String taskId) {
|
||||||
jumpIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); // 避免创建重复页面
|
if (context == null || taskId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NotificationManager notificationManager =
|
||||||
|
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
if (notificationManager != null) {
|
||||||
|
notificationManager.cancel(getNotificationId(taskId)); // 取消后循环铃声自动停止
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 步骤3:创建PendingIntent(授权系统在用户点击时执行跳转)
|
// ---------------------- 前台服务通知(适配API 30,无声无震动) ----------------------
|
||||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
public static Notification createForegroundServiceNotification(Context context, String serviceStatus) {
|
||||||
context,
|
if (context == null) {
|
||||||
FOREGROUND_SERVICE_NOTIFICATION_ID, // 请求码与通知ID一致,确保唯一
|
throw new IllegalArgumentException("Context cannot be null for foreground service notification");
|
||||||
jumpIntent,
|
}
|
||||||
// 适配Android 6.0+:IMMUTABLE确保安全性,UPDATE_CURRENT确保Intent参数更新
|
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ?
|
|
||||||
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT :
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
|
||||||
);
|
|
||||||
|
|
||||||
// 步骤4:构建前台服务通知(低打扰、强关联服务状态)
|
// 确保前台服务渠道已创建(低打扰配置)
|
||||||
return new NotificationCompat.Builder(context, FOREGROUND_SERVICE_CHANNEL_ID)
|
NotificationManager notificationManager =
|
||||||
.setSmallIcon(R.mipmap.ic_launcher) // 必须设置(系统强制要求,建议用应用图标)
|
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
.setContentTitle("位置服务运行中") // 固定标题,用户快速识别服务类型
|
if (notificationManager != null) {
|
||||||
.setContentText(serviceStatus) // 动态内容(如“正在获取GPS位置”“已连续运行30分钟”)
|
createNotificationChannel(notificationManager);
|
||||||
.setContentIntent(pendingIntent) // 点击跳转至功能页
|
}
|
||||||
.setOngoing(true) // 关键:设置为“不可手动清除”(仅服务停止时能取消,符合前台服务规范)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_LOW) // 低优先级:不弹窗、不抢占通知栏焦点
|
|
||||||
.setDefaults(NotificationCompat.DEFAULT_SOUND)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// 点击跳转Intent
|
||||||
* (配套工具方法)更新前台服务通知的状态文本(如GPS获取进度、运行时长)
|
Intent jumpIntent = new Intent(context, LocationActivity.class);
|
||||||
* @param context 服务上下文
|
jumpIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||||
* @param newServiceStatus 新的状态文本(如“已获取最新位置:北纬30.123°”)
|
|
||||||
*/
|
// PendingIntent(API 30必加IMMUTABLE)
|
||||||
public static void updateForegroundServiceStatus(Context context, String newServiceStatus) {
|
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||||
if (context == null) {
|
context,
|
||||||
return;
|
FOREGROUND_SERVICE_NOTIFICATION_ID,
|
||||||
}
|
jumpIntent,
|
||||||
// 重新创建通知(复用create方法,传入新状态文本)
|
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
Notification updatedNotification = createForegroundServiceNotification(context, newServiceStatus);
|
);
|
||||||
// 用相同ID更新通知(覆盖旧通知,实现状态刷新)
|
|
||||||
NotificationManager notificationManager =
|
// 构建前台服务通知(无声无震动,符合低打扰)
|
||||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
return new NotificationCompat.Builder(context, FOREGROUND_SERVICE_CHANNEL_ID)
|
||||||
if (notificationManager != null) {
|
.setSmallIcon(R.mipmap.ic_launcher)
|
||||||
notificationManager.notify(FOREGROUND_SERVICE_NOTIFICATION_ID, updatedNotification);
|
.setContentTitle("位置服务运行中")
|
||||||
}
|
.setContentText(serviceStatus)
|
||||||
}
|
.setContentIntent(pendingIntent)
|
||||||
|
.setOngoing(true) // 不可手动清除(前台服务规范)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||||
|
.setDefaults(0) // 禁用所有默认提醒(无声无震动)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------- 前台服务通知状态更新(适配API 30) ----------------------
|
||||||
|
public static void updateForegroundServiceStatus(Context context, String newServiceStatus) {
|
||||||
|
if (context == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Notification updatedNotification = createForegroundServiceNotification(context, newServiceStatus);
|
||||||
|
NotificationManager notificationManager =
|
||||||
|
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
if (notificationManager != null) {
|
||||||
|
notificationManager.notify(FOREGROUND_SERVICE_NOTIFICATION_ID, updatedNotification);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user