Repository navigation
Releases
v13.2 · 常驻通知也可消除
常驻通知(音乐 / 下载 / 导航 / 前台服务)现在也能被规则消除了
先说为什么之前「消不掉」
不是我们漏做了——代码里没有任何跳过常驻通知的逻辑,cancelNotification 是照常调的。
真正的原因是 Android 平台行为:常驻通知由应用的前台服务持有,取消掉之后几十毫秒内,
应用就会用完全相同的内容重发同一条通知。所以我们此前只能「来一条删一条」。
这次改用系统的「打盹」机制
对标了那个能做到「一次操作、长期有效、恢复要把时间调到一年后」的 App 后改道:
listener.snoozeNotification(key, 1年)
snooze 是系统侧的持久状态(存在 system_server 的 NotificationManagerService 里),
休眠期内同一条通知(同 key)再更新也不会显示——一次操作,长期有效。
项目的「推迟」动作底层本来就在调用同一个 API,所以零新增 API 依赖。
怎么用
规则编辑页 → 勾选「消除」→ 下面出现两个子开关:
| 开关 | 作用 |
|---|---|
| 静音 | 先把该通知所属渠道静音,再消除(渠道级,影响该渠道后续通知,原始设置已留底可恢复) |
| 常驻通知 | 命中带「进行中」标记的通知时,用系统打盹让它休眠约 1 年 |
「常驻通知」的语义(注意事项已在开关下方显示)
- 命中音乐 / 下载 / 导航 / 前台服务等常驻通知时,休眠约 1 年
- 休眠期内同一条通知再更新(比如播放进度)也不会回来,且不需要每次重发都删一次
- 只影响这一条通知,不影响该应用的其他常驻通知
- 到期后自动恢复,届时规则会重新判定(所以想提前恢复,把系统时间往后调即可)
- 若应用改用别的通知或方式展示,可能仍压不住;系统自身的常驻通知(系统更新、安全提示等)不建议消除
实现上守住的两条
- 不会「永远不恢复」:snooze 期间源 App 每秒更新进度,若那时再打盹一次会把到期时间
不断刷新。所以加了一张抑制表,抑制期内直接跳过、不重复打盹;到期自动移除并恢复判定。 - 不违反 V11.5 红线:不是「丢弃式节流」——内容一次不丢,只是被抑制的那条不再反复出现。
其它
- 新增
BlockOngoingTest8 例(含「旧规则 JSON 无blockOngoing键 → false」的零迁移断言) - 489 单测全绿;versionCode 24 / versionName 13.2
- 真机验收重点:① 常驻通知是否真的消失且不再回来;② 同 App 的其他常驻通知是否不受影响;
③ 取消勾选后 / 关闭总开关后,之前被压住的常驻通知是否能恢复显示
apk sha256: 5507415b3540275e5a2c72cee8dd81568416339b3c1c2cc467de3b679025838d
签名证书 SHA-256: ecff994dd1a1bc6e4609088d1ca0f8b8394a63bf6a27c5b70b2fe8d58587d572
v13.1 · 耗电优化
耗电优化(保留全部功能)
针对「耗电 18%」做了全仓性能审计,定位到 8 处放大器,全部只降开销不改语义:
🔴 最大的一处
- 媒体兜底轮询原本无条件常开:
attach()里就启动了 15 秒一次的循环,
即使没有任何规则引用media.*也照跑 —— 一天 5760 次空转唤醒,
而前台服务不让进程进入 Doze,这些唤醒全部按满速执行。
现在由setPollingEnabled()随开关启停,没用到就循环根本不存在;
同时把播放中 500ms → 1s、空闲 15s → 60s(歌词换句粒度 2~5 秒,1 秒足够跟手)。
🔴 放歌时的放大器
- 媒体内容一变就重扫整个通知栏:一次 = 1 次 binder + 每条通知的全量 extras dump
- 黑名单查询 + 可能的数据库写。几十条 × 每 2~5 秒 = 每分钟上千次写。
现在改为 1.5 秒合并执行(跑的永远是最新一次,不是丢弃——
V11.5 的教训是丢弃会永久丢内容),并对「没有流水行、且没有 media 规则作用域可能匹配」
的通知直接跳过 extras dump。
- 黑名单查询 + 可能的数据库写。几十条 × 每 2~5 秒 = 每分钟上千次写。
🟠 每条通知都在做的重复工作
- 结论同值也写库:进度/音乐通知每秒刷新,命中的规则却不变 → 每秒写一组完全相同的值。
现在统一走writeResult(),签名相同一次 UPDATE 都不发。 stats_daily每条通知 3 条写语句:新行不再重复 UPDATE appName,
且refreshAppName加了AND appName <> ?(名字没变就不重写那一行)。- 黑名单每条通知一次
SELECT EXISTS→ 进程内 Set 缓存(写操作失效)。 - 总开关每条通知一次 DataStore 收集 →
SettingsStore进程内快照
(唯一写入点是setMasterEnabled,备份恢复也走它,不会陈旧)。 - extras 指纹对每个值跑一次正则 → 改成常量时间判定(媒体类通知 30~50 个键)。
🟡 其它
- 弹幕同屏上限 24 → 12(每条都是滚动期间每帧重绘的半透明全屏宽图层;
12 仍远超「超过 5 条」的原始要求)。若你需要 24,告诉我改回来。 - 命中日志
i→d(release 下不再写 logd)。
想进一步优化?告诉我这三项,我可以按你的用法再收窄
- 有没有规则勾选了
media.*字段(决定媒体采集是否开启); - 常驻通知(前台服务)开关是开着还是关着;
- 主要用弹幕 / 悬浮,还是只用消除和替换。
其它
- 481 条单测全绿
- versionCode 23 / versionName 13.1
- 文档新增「耗电红线」(需求文档 §5 第 10 条)与使用说明常见问答
apk sha256: 86a5a281ea6f99987da409030319f7c8203e08d1d74bfbcb06ed295af100a2a6
签名证书 SHA-256: ecff994dd1a1bc6e4609088d1ca0f8b8394a63bf6a27c5b70b2fe8d58587d572
v13.0 · 规则分组
规则分组(归类标签)
一条规则可以打上任意多个分组标签,规则列表顶部出现「全部 + 各分组」的 tab,点一下只看该分组。
- 编辑页新增「分组(归类标签)」区块:新建分组、点已有分组一键加入、已选标签点一下移除
- 卡片上列出该规则所属的全部分组
- 分组只是归类,完全不影响匹配判定——同一条规则在多个分组 tab 下行为一致
- 排序仍是全局优先级:只有「全部」tab 下可拖动调顺序(分组视图会提示切回「全部」)
- 零迁移:旧规则 JSON 无
groups键 → 空列表,覆盖安装数据不丢
其它
- 481 条单测全绿(新增 10 条
RuleGroups纯函数单测) - versionCode 22 / versionName 13.0
apk sha256: a5e6c48509a9236425172c0ed1ca0791473ea553f4b678da2ead98cbba0e3461
签名证书 SHA-256: ecff994dd1a1bc6e4609088d1ca0f8b8394a63bf6a27c5b70b2fe8d58587d572
v12.2 — 分组匹配模式
新增:分组匹配模式(TEXT_GROUPS)
关键词分多组:组间 AND(每组都要满足),组内可选「任一出现 / 全部出现」。
典型用法——「必须出现 true,且 salt、nest 任一出现」:
- 第 1 组填
true - 第 2 组填
salt、nest,选「任一出现」
每个关键词在任一被选字段中查找,可以分别出现在不同字段(如 true 在标题、salt 在正文)。组可随意增删。
为什么加这个模式
既有七模式都表达不了「组间 AND + 组内 OR」:
- 包含全部 → salt 和 nest 必须都出现
- 包含任一 → 太松
- 包含 A 且不含 B → 第二组是否定
- 正则 → 逐字段匹配,不能跨字段组合
其他
- 规则编辑页「分组匹配」分支:每组一张卡片(组内模式二选一 + 删除组 + 关键词编辑)
- App 内「使用说明」同步该模式说明
- 旧规则 JSON 无该字段 → 自动零迁移,行为不变
- 471 项单元测试全绿
签名:CN=nf(证书 SHA-256 ecff994d…87d572,与历版同密钥库,可直接覆盖升级)
文件 SHA-256:9d14b7670ef38521207966a7d05bd49881736f6b52a9d3b3693d3aaef3b31849
v12.1 — 媒体字段实时驱动规则 + 条件目标规则多选 + 已选App可删
v12.1 — 媒体字段实时驱动规则 + 条件目标规则多选 + 已选 App 可删
三个来自真机使用的修复:
① media.playing 变化后规则立即生效(修「只有第一次生效」)
以前媒体字段变化时只会重跑「播放器自己包名、且已进过流水」的通知,导致:
- 引用
media.playing的规则只在首次命中时生效一次; - 用「检测 media.playing 为 false」做悬浮显示条件(命中时清除某规则的悬浮窗)完全不触发。
现在媒体字段变化时对通知栏全部存活通知重跑引用 media.* 的规则——匹配、动作、
命中事件全链路走通。典型场景:「暂停播放 → 撤掉某条规则的悬浮窗」即刻生效,
不需要播放器重发通知。已有流水的通知同时就地刷新内容;没有流水的存量通知只求值、不写记录。
② 悬浮显示条件的目标规则支持多选
「满足时作用于」从单选改为多选:勾选『当前规则』= 只作用于本条规则自己的悬浮窗;
多选其他规则 = 一次控制多扇窗(各用它们自己的样式与内容)。V12.0 配过的单选目标
会自动兼容保留。
③ 已选 App 可以删除了
规则作用范围(仅指定 App / 全局排除)与悬浮显示条件里的已选 App 标签,
现在点一下标签即可移除(标签带关闭图标),「选择 App」按钮仍用于增选。
升级说明
- 签名证书与历代一致(SHA-256 见下),可直接覆盖升级,规则 / 流水 / 设置不丢。
- minSdk 26(Android 8.0+);versionCode 20 / versionName 12.1。
校验信息
- 签名证书 SHA-256:
ecff994dd1a1bc6e4609088d1ca0f8b8394a63bf6a27c5b70b2fe8d58587d572 - 文件 SHA-256:
2b1a9b84b60ed1b2e6d5bda53f1a93d2ae66f57a501078077cd7cc47a15d3125
源码:https://github.com/zmn001125/notification-filter (GPL-3.0)
v12.0 — 悬浮窗一规则一窗并存 + 显示条件可指定目标规则
v12.0 — 悬浮窗一规则一窗并存 + 显示条件可指定目标规则
新功能
① 不同规则的悬浮窗可以同时存在,互不顶掉
- 以前所有规则共用同一个悬浮窗,后命中的规则会把前一个顶掉。
- 现在每条规则拥有自己的悬浮窗(按规则 id 区分),可以同时显示多个,各自独立。
- 同一个规则连续命中时仍然是「原地换内容」(不闪烁、不跳位置,沿用 V9.3 行为)。
- 两条规则恰好配了同一个停靠位置时,窗口会按 14dp 逐级错开,不会完全重叠。
- 同时最多 8 个悬浮窗(安全上限,超出撤最早的);点「清除屏幕显示」全部撤掉。
② 悬浮显示条件可以指定「作用到哪条规则」
- 每条显示条件新增「满足时作用于」选项:
- 留空 = 当前规则(默认,与以前完全一致);
- 选某条规则 = 条件成立时显示/清除那条规则自己的悬浮窗(用那条规则自己的样式与内容)。
- 例如:规则 A(任意通知命中)的条件指向规则 B(天气规则),则任意通知出现时弹出天气悬浮窗。
- 「立即清除悬浮」现在也只撤对应的那一个窗,不再波及别人的窗口。
③ 媒体字段采集性能优化
- 媒体会话字段的读取从主线程下沉到协程线程批量回写,播放中每秒读取不再占主线程。
安装
- 下载
notification-filter-v12.0-release.apk(约 12.7 MB)。 - 卸载旧版或直接覆盖安装(签名证书未变,SHA-256 见下方,覆盖升级不会丢数据)。
- minSdk 26(Android 8.0+);versionCode 19 / versionName 12.0。
校验信息
- 签名证书 SHA-256:
ecff994dd1a1bc6e4609088d1ca0f8b8394a63bf6a27c5b70b2fe8d58587d572(与历代版本一致) - 文件 SHA-256:
fb4265feabaad45fc636a5d947b9c589ad00f8e7d1f56edcfad65fad47773f6a
说明
- 纯本地运行、不联网(仅备份/恢复功能在使用云备份时才请求 INTERNET 权限)。
- 源码:https://github.com/zmn001125/notification-filter (GPL-3.0)
v11.5 — 内容变化时规则/动作真正重跑(移除 1.2s 节流)
上一版只修好了「流水显示」,规则本身仍被 1.2 秒节流挡住,所以你会看到「只拿到第一次出现的内容」。这一版把节流整层去掉了。
改了什么
删掉「同一通知 1.2 秒内只处理一次」的时间窗节流。它在窗口内会整条丢弃更新,后果有两个:
- 只更新一次的通知(如「待取件」→「已签收」)内容永久丢失;
- 规则完全不跟着跑 —— 规则匹配、内容提取/聚合(
{reN}/{itemsN})、动作(弹幕/悬浮/替换/朗读)都拿不到新内容。
现在:只要内容变了,就完整跑一遍
内容指纹判定(任意字段变化)→ 规则匹配 → 内容提取 / 聚合录制 → 动作执行 → 流水就地刷新
弹幕 / 悬浮 / 替换 / 朗读都会用变化后的新内容重新执行。
刷屏怎么保证
不靠节流,靠两道已有机制:
- 指纹去重:内容完全相同的重复投递照样整条跳过(不会白跑);
- 合并成一行:一条存活通知在流水里始终只占一行,内容原地刷新,不新增行、不跳位置。
现在会按变化频率真实执行(这正是"每次变化都要拿到"的语义)。嫌吵的话,规则里加个「设备状态 /
作用范围」收窄,或者告诉我,我给动作单独加一个「渲染结果相同时不重复执行」的去重。
安装
notification-filter-v11.5-release.apk · versionCode 15 · Android 8.0+ · 覆盖安装即可(同签名)
- 签名证书 SHA-256:
ECFF994DD1A1BC6E4609088D1CA0F8B8394A63BF6A27C5B70B2FE8D58587D572 - 文件 SHA-256:
fdf21ae2c964641fa468f3ea6da546124c37ee344a270b3281569a48fab8e2c0
验证
450 例单测全绿。本次是「去掉一层逻辑」,改动集中在 NotificationPipeline:
删除 recentHandledAt、MIN_HANDLE_INTERVAL_MS 与节流分支(净 -64 行),
保留指纹去重与 upsertRecord 合并。
许可:GPL-3.0(二改后分发须同样开源)。
v11.4 — 修复通知内容实时更新 + Android 正则断言兼容
修两个真问题:通知内容更新拿不到、正则断言在手机上不生效。
1)通知还在、字段也在变,但列表一直显示旧内容
根因有两层,都修了:
- 内容指纹(判断"这条通知是不是新内容")此前只签了
android.progress / progressMax。
其它 extras 字段变化不改变指纹 → 被当成"内容没变"整条丢弃,流水、详情页、规则全都拿不到新值。
→ 现在任意字段变化都算内容变了(签名覆盖全部 extras)。 - 防刷屏的 1.2 秒节流此前直接丢弃整条通知。如果一条通知只更新一次
(比如取件通知从"待取件"变成"已签收"),这次更新恰好落在窗口内就永久消失。
→ 现在节流只压"跑规则 + 执行动作",内容照样就地刷新到那一行:
不新增行、不跳位置、不计统计、不触发"出现"边沿,动作频率仍是 1.2 秒一次。
刷屏没有回退:同一通知始终只占一行,内容原地更新。
2)正则的正向 / 负向断言"不能用"
根因:Android 的正则引擎是 ICU4C,后行断言里不允许出现无上限量词(* + {n,}),
而桌面 JDK 13+ 已经放开。所以 (?<=取件码[::]?\s*)\d{6} 这类写法在电脑/在线工具上跑得好好的,
到手机上编译直接失败,表现就是"断言不支持"。
修法:新增兼容层 RegexCompat
- 编译失败时把
(?<=X)P等价改写成(?:X)P(仅当 X 含无上限量词),改写结果进缓存;
对"是否命中"和"按捕获组$N取值"完全等价(差别只是$0会多含 X 那段文字)。 - 负向后行
(?<!X)无法等价改写 → 不改,但给出明确改法。 - 保存校验、试跑面板不再只说"正则语法有误",而是直接说原因:
后行断言长度无上限 / 全角符号(({*)/\g{n}等。
仍然成立的一条口径(不是 bug):断言只在单个字段内成立。标题里的「取件」+ 正文里的号码
无法被一个后行断言串起来——请拆成两条规则,或把文字放进捕获组。
安装
notification-filter-v11.4-release.apk · versionCode 14 · Android 8.0+ · 覆盖安装即可(同签名)
- 签名证书 SHA-256:
ECFF994DD1A1BC6E4609088D1CA0F8B8394A63BF6A27C5B70B2FE8D58587D572 - 文件 SHA-256:
b607b5bce0a8c94783927b31d3bc364744e3960778ce8107854a436f7d393a64
验证
450 例单测全绿,新增三组针对本次修复的用例:
RegexCompatTest(断言改写/诊断)、RegexLookaroundTest(四种断言在规则与提取两条链路上都可用)、
ExtrasSignatureTest(任意 extras 变化必须改变指纹;ByteArray 的 identityHash 不参与)。
许可:GPL-3.0(二改后分发须同样开源)。
v11.3 — 修正启动图标(铃舌居中、三横不再被裁)
启动图标修正版。功能与 v11.2 完全相同,只改了图标绘制,已装 v11.2 的可以直接覆盖升级(同一签名,数据保留)。
修了什么
| 问题 | 现在 |
|---|---|
铃舌(小圆)圆心在 x=47,而铃身中心是 54 —— 肉眼可见偏左 |
圆心归到 54,与铃身同心 |
右侧三条内容线到 x=90,超出 adaptive icon 半径 36 的安全圆(右端距离圆心 39.4)→ 被启动器圆角/圆形遮罩裁掉,且三条切口不齐 |
收进 x=76..85、宽 9、圆角 2,y=42/50/58 等距;最远点 33.2 —— 任何遮罩都不会切边 |
改动文件只有一个:app/src/main/res/drawable/ic_launcher_foreground.xml。
安装
- 下载
notification-filter-v11.3-release.apk - 传到手机点击安装(覆盖安装即可,无需卸载)
- 若桌面图标没变,长按图标 → 编辑,或重启启动器(部分启动器会缓存图标)
- 适用:Android 8.0+(
minSdk 26)| 包名com.local.notificationfilter|versionCode 13 - 签名证书 SHA-256:
ECFF994DD1A1BC6E4609088D1CA0F8B8394A63BF6A27C5B70B2FE8D58587D572(与 v11.2 相同)
文件校验
SHA-256: 4f0af80379ee5e5b64e8108ebdb8797f9f531c261cfcebc195223ce53fbee45e
许可
GPL-3.0:二改后分发(发布改过的 APK / 公开代码)必须同样以 GPL-3.0 开源全部源码。
v11.2 — 首个公开发布(Android 通知过滤 App)
Android 通知过滤 App v11.2 —— 首个公开发布版本(侧载 APK)。
安装
- 下载下方
notification-filter-v11.2-release.apk - 传到手机后点击安装(系统会提示"未知来源",允许你使用的文件管理器即可)
- 打开 App,按首页权限引导授予通知使用权(必须),再打开总开关
- 适用:Android 8.0 及以上(
minSdk 26),在小米 15 / 澎湃 OS3 上开发与验证 - 包名:
com.local.notificationfilter|versionCode 12 - 签名证书 SHA-256:
ECFF994DD1A1BC6E4609088D1CA0F8B8394A63BF6A27C5B70B2FE8D58587D572
文件校验
SHA-256: 00dc204662c30bb45c79456e80c98d589c5e424b1b104a52c705489bd7246747
这一版有什么
- 规则引擎:7 种匹配模式(全部内容 / 包含任一 / 包含全部 / 不包含任一 / 不包含全部 / 包含 A 且不含 B / 正则)
- 匹配字段:标题、正文、消息列表、副标题、摘要、发送者 + 通知元信息 + 任意 extras 字段 +
media.*媒体字段 - 六种动作:消除(可静音渠道)、推迟、替换、朗读、弹幕、悬浮(可叠加,失败留痕)
- 内容提取:正则抓取,
{reN}当前通知即时提取、{itemsN}跨通知累积 - 备份恢复:本地文件夹(SAF)+ 坚果云 WebDAV,支持自动备份与保留策略
- 通知流水、按天统计、全量搜索、六套主题、App 内使用说明
说明
- 纯本地运行:不配置 WebDAV 就不会发起任何网络请求,无账号、无埋点、无第三方 SDK
- 不提供担保,使用风险自负;升级需覆盖安装(同一签名),降级需先卸载(会清空数据,请先备份)
- 许可:GPL-3.0。二改后分发(发布改过的 APK / 公开代码)必须同样以 GPL-3.0 开源全部源码
完整使用手册见仓库 docs/使用说明.md