Skip to content

Releases: madeye/ad-skipper

广告跳过 v1.3

Choose a tag to compare

@madeye madeye released this 09 Aug 02:10
5c62639

防误触重构版本:图像检测层从『时间窗内放行』改为『有证据才开火』,没有开屏广告的 App 不再有任何被误点的可能;真机回归验证豆瓣/虎扑跳过正常。

广告证据门控(#14)

图像层(YOLO/VLM)点击现在需要会话内先确认『屏幕上真的是广告』,三种独立信号任一即可:

  • 「广告」角标:监管要求开屏广告必须标注;复用 L2 已在跑的 OCR,一次推理两用,零新增开销;
  • 递减倒计时:同一屏幕区域的数字跨轮询递减——正常界面几乎不存在此特征;时钟、电量、价格等被解析规则排除。确认后轮询窗口按剩余秒数动态延长(上限 90s),覆盖超长广告;
  • 广告 SDK 指纹:穿山甲/优量汇/快手联盟/百青藤/华为/小米/OPPO/vivo 等的窗口类名与 view id。

L1/L2 关键词层保持不设门控——精确且是无可读标记广告的兜底,已有场景不回退。

无广告 App 自动降级(#14)

连续 15 次无广告证据的启动会话后,该 App 降为 L1-only(不截屏、不 OCR、无图像层),近乎零开销;每第 5 次会话全量探测,SDK 信号即刻解除降级,App 更新自动重置。

豆瓣回归修复(#16)

豆瓣把信息流预载在广告下层,广告屏节点树 >30,#14 的全时段树检查曾把图像层整个闪屏期关闭。现核心 8s 窗口内仅证据门控决定,8s 后树检查继续保护信息流。真机验证:豆瓣冷启动 2–4s 内跳过,45s 窗口零误点。

诊断增强(#16)

手势注入结果(completed/CANCELLED)写入日志——『点了没反应』和『成功点击』此前在日志里不可区分。

安装包为 keystore 签名的 release 构建(minify + shrinkResources)。

广告跳过 v1.2

Choose a tag to compare

@madeye madeye released this 08 Aug 14:19
5fb8e8e

浏览器防误点版本:无障碍服务不再于浏览器类应用中运行检测或触发点击。

浏览器排除(#12)

网页内容里到处是「跳过 / Skip」文字和广告样式的图片,三层检测(节点关键词 / OCR / YOLO·VLM)都可能命中并误点——一次误点就可能导航离开或误触正在阅读的页面。本版本起浏览器被整体排除在检测管线之外:

  • 动态识别:服务启动时通过 ACTION_VIEW + BROWSABLE + 裸 http URL 解析设备上的浏览器;只有无 host 过滤的浏览器会匹配,普通 app 的 deep-link 不受影响。
  • 静态兜底:Chrome、Firefox、Edge、Samsung Internet、各 OEM 浏览器(小米/OPPO/vivo/华为)、QQ 浏览器、百度浏览器等常见包名直接排除,覆盖包可见性或 role 机制导致查询漏掉的情况。
  • 例外:UC 浏览器与夸克自身有闪屏广告,保留在检测管线中,启动闪屏照常跳过。
  • 排除在事件入口处短路,被排除的浏览器连截屏都不会发生,无额外开销。

安装包为 keystore 签名的 release 构建(minify + shrinkResources)。

广告跳过 v1.1

Choose a tag to compare

@madeye madeye released this 08 Aug 09:45
6d296e2

Real-device hardening release: v1.0 worked flawlessly on the emulator and then met HyperOS. Everything below came out of on-device debugging of splash ads that escaped detection entirely (Douban, Hupu) on a Xiaomi 14 Ultra.

Survive OEM process freezing (the big one)

HyperOS's GreezeManager cgroup-freezes the accessibility service ~1 second after its last accessibility event — and never thaws it to deliver new events. A silent video splash ad emits no events, so the service slept through entire ads; wake locks are force-disabled while frozen and background-run exemptions don't help.

  • Always-on thaw pulse: an invisible 1px accessibility overlay emits a content-changed event every 300 ms while the screen is on, keeping the freezer's inactivity timer from ever firing. Stops on screen-off; self-resumes after any thaw (pending handler messages survive a freeze).
  • Wake-up watchdog: a 60 s non-wakeup exact alarm — alarm delivery is the one thing the freezer reliably thaws for. Zero battery cost while asleep; an alarm that expired during sleep fires the instant the device wakes, restarting the pulse exactly when needed.

Cover late-appearing, long-running ads

Some apps (Hupu) sit ~8 s on a white splash before the ad appears, then play it silently for ~40 s — the old fixed 8 s detection window provided zero coverage.

  • Splash polling now runs up to 45 s; past the first 8 s, image-based detection (L3) only stays active while the screen still looks like a splash (tiny accessibility tree), so app UI never gets false-positive taps.
  • YOLO detections centered in the bottom-center branding strip are vetoed — splash logos were being mistaken for skip buttons; real skip buttons hug a corner.

Correctness fixes

  • Detection and taps only happen while the target app owns the active window — transient system dialogs during cold starts can no longer redirect taps.
  • Keyword matches covering more than ⅓ of the screen are rejected (Douban ships a full-screen container literally named skip; tapping its center opened the ad).
  • takeScreenshot gets a 2 s timeout — on some transitions its callback never fires, which used to wedge the whole pipeline.
  • YOLO's ncnn engine warms up shortly after service connect instead of paying ~3 s cold-init inside the first splash window.

Packaging

  • Release builds are now signed via a keystore configured in local.properties.
  • Debug builds can dump every frame the pipeline sees to filesDir/shots/ (debug overlay toggle) for diagnosing detector misses.

Verified on device: consecutive Douban cold starts skipped in ~3 s via OCR, before the ad countdown ended; no false-positive taps across 100+ polling ticks on real app UI.

广告跳过 v1.0

Choose a tag to compare

@madeye madeye released this 08 Aug 03:29

First release of 广告跳过 (Ad Skipper) — an Android accessibility service that automatically taps the 跳过 button on app splash ads. Everything runs on-device; the app has no ad-related network traffic.

2026-08-08 refresh: the attached APK was rebuilt from d0d7c25 and now includes the L2 hardening from #7 — the ML Kit OCR client is a guarded process-wide singleton, so on ROMs where client construction fails (stripped-down AOSP builds), L2 self-disables cleanly instead of aborting whole detection passes, and the healthy path no longer creates a recognizer client per pass.

Detection pipeline

Four detectors run as a short-circuiting cascade — each layer only runs when the previous one misses:

Layer Method Latency Covers
L1 Accessibility node-tree keyword match <10 ms Standard buttons with 跳过/Skip text
L2 On-device OCR (ML Kit, Chinese model) ~100 ms Flutter/Unity/games that hide their UI tree
L3a Bundled YOLO11n skip-button detector on ncnn (Vulkan compute, CPU/NEON fallback, ~5 MB, mAP50 0.992) ~20–300 ms Countdown rings, styled/image-only buttons
L3b Local VLM grounding (llama.cpp + InternVL3-2B GGUF, optional in-app download) ~2–4 s Long-tail cases YOLO misses

Highlights

  • Splash-window polling — detection runs on a fixed 750 ms cadence for the first 8 s of each app session instead of relying on accessibility events. This wins the race against splash ads that render after the launch transition and emit no events (verified against real-world hot-start splash ads on MIUI), skips the first 500 ms where OEM starting-window snapshots show stale frames, and re-taps surviving targets (max 3 per session).
  • False-positive guards — image-based layers only run inside the splash window; launchers, home-screen search overlays and the minus-one screen are never touched; the app's own label is excluded from keyword matching; per-package cooldowns prevent tap loops.
  • Foreground keepalive service so aggressive OEM task killers don't reap the service.
  • Built-in verification — a mock splash ad (首页 → 模拟广告测试, enable 自测模式 in 设置) exercises the full pipeline end-to-end; per-app/per-layer skip statistics on the 统计 tab.
  • Configurable — keyword list, per-app whitelist, per-layer toggles, VLM thread count/timeout, debug overlay.

Requirements

  • Android 11+ (API 30, needed for the accessibility screenshot API)
  • arm64-v8a
  • No Google Play services needed — L2 uses the bundled ML Kit model (in-APK), and degrades gracefully to L1+YOLO if unavailable

Install

  1. Download ad-skipper-v1.0.apk below and install it.
  2. Open the app and follow the prompt to enable the accessibility service (设置 → 无障碍 → 广告跳过).
  3. Optional: download the InternVL3-2B model on the 模型 tab to enable the VLM fallback layer.

APK versionCode 2026080812, versionName 1.0, signed with:

SHA-256 (cert): 55:40:C1:F2:4F:5D:44:86:C5:96:EF:76:0A:8C:E9:8C:8E:68:FE:45:AF:C9:E9:54:1C:C6:3D:EF:3E:7A:76:B5
SHA-256 (apk):  9dd3b5bd186866b844c002fdb44b8cdb5970b3b61c044deae632491a95f116d8

Known limitations

  • Image-based layers (YOLO/VLM) are deliberately restricted to the 8-second splash window after an app (re)start; mid-app ads are handled by the keyword layers only.
  • The VLM layer stays inactive until a model is downloaded in the 模型 tab.
  • On MIUI/HyperOS, lock the app in recents and disable battery restrictions for reliable long-term operation.