添加通知栏
This commit is contained in:
@@ -32,6 +32,7 @@ import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import cc.winboll.studio.positions.utils.NotificationUtils;
|
||||
|
||||
public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
// 常量定义(不变)
|
||||
@@ -151,6 +152,11 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||
bindRealDistance(distanceView, targetModel);
|
||||
}
|
||||
}
|
||||
for (PositionTaskModel task : mAllPositionTasks) {
|
||||
if (task.isBingo() && task.isEnable()) {
|
||||
NotificationUtils.show(mContext, task.getTaskId(), task.getPositionId(), task.getTaskDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -467,11 +473,11 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
|
||||
|
||||
// 根据任务条件判断新触发状态
|
||||
if (task.isGreaterThan()) {
|
||||
newBingoState = distanceM > task.getDiscussDistance();
|
||||
newBingoState = task.isEnable() && distanceM > task.getDiscussDistance();
|
||||
} else if (task.isLessThan()) {
|
||||
newBingoState = distanceM < task.getDiscussDistance();
|
||||
newBingoState = task.isEnable() && distanceM < task.getDiscussDistance();
|
||||
} else {
|
||||
newBingoState = true;
|
||||
newBingoState = task.isEnable() && true;
|
||||
}
|
||||
|
||||
// 仅当状态变化时,才更新任务状态并标记需要通知
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package cc.winboll.studio.positions.utils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/09/30 16:09
|
||||
* @Describe NotificationUtils
|
||||
*/
|
||||
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.os.Build;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import cc.winboll.studio.positions.R;
|
||||
import cc.winboll.studio.positions.activities.LocationActivity; // 替换为你实际的LocationActivity路径
|
||||
|
||||
/**
|
||||
* 通知栏工具类:专注于任务相关通知的显示与点击跳转
|
||||
* 核心功能:显示任务描述通知,点击后携带positionId/taskId跳转到LocationActivity
|
||||
*/
|
||||
public class NotificationUtils {
|
||||
public static final String TAG = "NotificationUtils";
|
||||
// 1. 通知渠道ID(适配Android O及以上,必须唯一)
|
||||
private static final String TASK_NOTIFICATION_CHANNEL_ID = "task_notification_channel_01";
|
||||
// 2. 通知渠道名称(用户在系统设置中看到的名称)
|
||||
private static final String TASK_NOTIFICATION_CHANNEL_NAME = "任务通知";
|
||||
// 3. 通知ID(确保每次显示通知的ID唯一,这里用taskId哈希值避免重复)
|
||||
private static int getNotificationId(String taskId) {
|
||||
// 用taskId生成唯一ID(避免不同任务通知相互覆盖)
|
||||
return taskId.hashCode() & 0xFFFFFF; // 限制ID在0-16777215范围内(系统推荐)
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示任务通知
|
||||
* @param context 上下文(如Activity/Fragment,确保非空)
|
||||
* @param taskId 任务ID(用于跳转传递+生成唯一通知ID)
|
||||
* @param positionId 位置ID(用于跳转传递给LocationActivity)
|
||||
* @param taskDescription 通知显示内容(仅展示该文本给用户)
|
||||
*/
|
||||
public static void show(Context context, String taskId, String positionId, String taskDescription) {
|
||||
// 安全校验:避免空指针(上下文/必要参数为空时不执行)
|
||||
if (context == null || taskId == null || positionId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 步骤1:初始化通知管理器(系统服务,负责发送/取消通知)
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (notificationManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 步骤2:创建通知渠道(Android O及以上必须,否则通知不显示)
|
||||
createNotificationChannel(notificationManager);
|
||||
|
||||
// 步骤3:构建“点击通知跳转”的Intent(携带positionId和taskId)
|
||||
Intent jumpIntent = new Intent(context, LocationActivity.class);
|
||||
// 传递参数:用Intent的Extra携带ID(键名建议定义为常量,避免拼写错误)
|
||||
jumpIntent.putExtra("EXTRA_POSITION_ID", positionId);
|
||||
jumpIntent.putExtra("EXTRA_TASK_ID", taskId);
|
||||
// 配置Intent:清除栈内旧Activity,确保跳转后是最新页面(根据需求调整)
|
||||
jumpIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
|
||||
// 步骤4:创建PendingIntent(授权系统在用户点击通知时执行jumpIntent)
|
||||
// 注意:PendingIntent.FLAG_UPDATE_CURRENT 确保参数更新时覆盖旧Intent
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
getNotificationId(taskId), // 请求码:与通知ID一致,确保唯一
|
||||
jumpIntent,
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ?
|
||||
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT :
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
);
|
||||
|
||||
// 步骤5:构建通知内容(仅显示taskDescription,样式简洁)
|
||||
Notification notification = new NotificationCompat.Builder(context, TASK_NOTIFICATION_CHANNEL_ID)
|
||||
.setSmallIcon(R.mipmap.ic_launcher) // 通知小图标(必须设置,建议用应用图标)
|
||||
.setContentTitle("任务提醒") // 通知标题(固定,用户快速识别通知类型)
|
||||
.setContentText(taskDescription) // 通知内容(仅显示传入的任务描述)
|
||||
.setContentIntent(pendingIntent) // 绑定点击跳转事件
|
||||
.setAutoCancel(true) // 点击通知后自动消失(符合用户习惯)
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT) // 通知优先级(默认,不抢占焦点)
|
||||
.setDefaults(NotificationCompat.DEFAULT_SOUND) // 默认提示音(可选,根据需求关闭)
|
||||
.build();
|
||||
|
||||
// 步骤6:发送通知(用唯一ID确保不同任务通知不覆盖)
|
||||
notificationManager.notify(getNotificationId(taskId), notification);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建通知渠道(Android O/API 26及以上必须)
|
||||
* 作用:用户可在系统设置中管理该渠道的通知(如关闭声音/屏蔽)
|
||||
*/
|
||||
private static void createNotificationChannel(NotificationManager notificationManager) {
|
||||
// 仅在Android O及以上执行(低版本不需要渠道)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// 配置渠道:重要性为“默认”(不弹窗,仅通知栏显示+提示音)
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
TASK_NOTIFICATION_CHANNEL_ID,
|
||||
TASK_NOTIFICATION_CHANNEL_NAME,
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
);
|
||||
// 可选:配置渠道其他属性(根据需求调整)
|
||||
channel.setDescription("接收任务相关提醒,点击可查看任务详情"); // 渠道描述(系统设置中显示)
|
||||
channel.enableVibration(true); // 是否震动(默认开启,可关闭)
|
||||
channel.setVibrationPattern(new long[]{0, 300}); // 震动模式(0延迟,震动300ms)
|
||||
|
||||
// 注册渠道到系统(仅需注册一次,重复注册会覆盖旧配置)
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (可选)取消指定任务的通知
|
||||
* 场景:任务完成后关闭已显示的通知(如bingo状态取消后)
|
||||
*/
|
||||
public static void cancel(Context context, String taskId) {
|
||||
if (context == null || taskId == null) {
|
||||
return;
|
||||
}
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (notificationManager != null) {
|
||||
notificationManager.cancel(getNotificationId(taskId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,14 +212,14 @@ public class PositionTaskListView extends LinearLayout {
|
||||
// 核心:根据 isBingo 控制红点显示(true=显示,false=隐藏)
|
||||
simpleHolder.vBingoDot.setVisibility(task.isBingo() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
// 编辑模式:沿用原有绑定逻辑(不变)
|
||||
// 编辑模式:沿用原有绑定逻辑(核心修复在此处)
|
||||
else if (holder instanceof TaskContentViewHolder) {
|
||||
TaskContentViewHolder contentHolder = (TaskContentViewHolder) holder;
|
||||
bindTaskData(contentHolder, task, position);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 原有编辑模式绑定逻辑(完全不变) ----------------------
|
||||
// ---------------------- 核心修复:编辑模式绑定逻辑(解决布局中通知异常) ----------------------
|
||||
private void bindTaskData(final TaskContentViewHolder holder, final PositionTaskModel task, final int position) {
|
||||
String taskDesc = (task.getTaskDescription() == null) ? "未设置描述" : task.getTaskDescription();
|
||||
holder.tvTaskDesc.setText(String.format("任务:%s", taskDesc));
|
||||
@@ -227,6 +227,8 @@ public class PositionTaskListView extends LinearLayout {
|
||||
String distanceCondition = task.isGreaterThan() ? "大于" : "小于";
|
||||
holder.tvTaskDistance.setText(String.format("条件:%s %d 米", distanceCondition, task.getDiscussDistance()));
|
||||
|
||||
// 修复点1:先移除开关监听,再设置状态(避免设值时触发回调导致异常)
|
||||
holder.cbTaskEnable.setOnCheckedChangeListener(null);
|
||||
holder.cbTaskEnable.setChecked(task.isEnable());
|
||||
holder.cbTaskEnable.setEnabled(mCurrentViewMode == VIEW_MODE_EDIT);
|
||||
|
||||
@@ -234,6 +236,7 @@ public class PositionTaskListView extends LinearLayout {
|
||||
holder.btnEditTask.setVisibility(View.VISIBLE);
|
||||
holder.btnDeleteTask.setVisibility(View.VISIBLE);
|
||||
|
||||
// 删除按钮逻辑(不变,本身在点击时执行,不涉及布局中通知)
|
||||
holder.btnDeleteTask.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -247,6 +250,7 @@ public class PositionTaskListView extends LinearLayout {
|
||||
}
|
||||
});
|
||||
|
||||
// 编辑按钮逻辑(不变,弹窗保存时修复通知时机)
|
||||
holder.btnEditTask.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -254,15 +258,21 @@ public class PositionTaskListView extends LinearLayout {
|
||||
}
|
||||
});
|
||||
|
||||
holder.cbTaskEnable.setOnCheckedChangeListener(null);
|
||||
// 修复点2:开关监听-用 RecyclerView.post 延迟执行 notify(避免布局中调用)
|
||||
holder.cbTaskEnable.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
task.setIsEnable(isChecked);
|
||||
notifyItemChanged(position);
|
||||
if (mOnTaskUpdatedListener != null && mBindPositionId != null) {
|
||||
mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mData));
|
||||
}
|
||||
task.setIsEnable(isChecked); // 先更新数据源(必须执行)
|
||||
// 关键:通过 mRvTasks.post 延迟通知,确保在布局计算/滚动结束后执行
|
||||
mRvTasks.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
notifyItemChanged(position);
|
||||
if (mOnTaskUpdatedListener != null && mBindPositionId != null) {
|
||||
mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mData));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -272,7 +282,7 @@ public class PositionTaskListView extends LinearLayout {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 原有编辑弹窗逻辑(完全不变) ----------------------
|
||||
// ---------------------- 修复:编辑弹窗保存逻辑(延迟通知,避免极端场景异常) ----------------------
|
||||
private void showTaskEditDialog(final PositionTaskModel task, final int position) {
|
||||
final Context context = getContext();
|
||||
View dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_edit_task, null);
|
||||
@@ -335,11 +345,16 @@ public class PositionTaskListView extends LinearLayout {
|
||||
task.setIsGreaterThan(isGreater);
|
||||
task.setPositionId(mBindPositionId);
|
||||
|
||||
notifyItemChanged(position);
|
||||
|
||||
if (mOnTaskUpdatedListener != null && mBindPositionId != null) {
|
||||
mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mData));
|
||||
}
|
||||
// 修复点3:弹窗保存后延迟通知(同开关逻辑,避免列表滚动时异常)
|
||||
mRvTasks.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
notifyItemChanged(position);
|
||||
if (mOnTaskUpdatedListener != null && mBindPositionId != null) {
|
||||
mOnTaskUpdatedListener.onTaskUpdated(mBindPositionId, new ArrayList<PositionTaskModel>(mData));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dialog.dismiss();
|
||||
Toast.makeText(context, "任务已更新", Toast.LENGTH_SHORT).show();
|
||||
@@ -347,7 +362,7 @@ public class PositionTaskListView extends LinearLayout {
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------- ViewHolder 定义(新增简单模式 Holder,适配红点) ----------------------
|
||||
// ---------------------- ViewHolder 定义(完全不变) ----------------------
|
||||
// 基础抽象 ViewHolder(不变)
|
||||
public abstract class TaskViewHolder extends RecyclerView.ViewHolder {
|
||||
public TaskViewHolder(@NonNull View itemView) {
|
||||
|
||||
Reference in New Issue
Block a user