Skip to content

feat: add ad/tracker blocker with auto-updating filter list - #101

Open
joshi-7373 wants to merge 2 commits into
kododake:mainfrom
joshi-7373:feature/adblocker
Open

feat: add ad/tracker blocker with auto-updating filter list#101
joshi-7373 wants to merge 2 commits into
kododake:mainfrom
joshi-7373:feature/adblocker

Conversation

@joshi-7373

@joshi-7373 joshi-7373 commented Jun 17, 2026

Copy link
Copy Markdown

Summary

Adds a built-in ad & tracker blocker to the in-app WebView — network blocking, cosmetic ad-hiding, and YouTube video-ad skipping. It is enabled by default and can be toggled under Settings → Privacy & Ads. The blocklist updates itself automatically from a public AdGuard list, so it needs no manual upkeep.

Why

Ad blocking is listed as a known gap in the README ("No Ad Blocking — contributions welcome"). This implements it natively and keeps the blocklist current on its own.

What changed

New files
web/AdBlocker.kt — blocking engine, blocklist parser, cosmetic CSS, and the YouTube ad-skip script.
web/FilterListUpdater.kt — weekly background download and caching of the AdGuard DNS filter.
assets/adblock_hosts.txt — bundled fallback blocklist.

Modified
web/ConfiguredWebView.kt — blocks requests in shouldInterceptRequest and injects the cosmetic + YouTube scripts in onPageFinished.
settings/SettingsViews.kt — adds the "Privacy & Ads" toggle.
data/BrowserPreferences.kt — persists the enabled preference.
res/values/strings.xml — new setting strings.
README.md — features, current issues, and a privacy note.

How it works

Network blocking — drops requests to known ad/tracker hosts (subdomain-aware) plus a few ad URL paths, returning an empty 204. The blocklist merges a hard-coded core list, the bundled asset, and the auto-updating AdGuard list. The parser reads both hosts format and AdGuard domain rules, safely skipping exceptions, cosmetic, and modifier rules.

Cosmetic CSS — conservative selectors hide leftover empty ad boxes.

YouTube — auto-clicks Skip, fast-forwards un-skippable ads, and removes banner/overlay ads. It acts only when YouTube flags the player as showing an ad, targets the player's own video element, and saves/restores the mute state so real videos are never left muted.

Limitations

Server-stitched (SSAI) YouTube ads cannot be removed inside a WebView, and the YouTube skip selectors may need occasional updates as YouTube changes.

Notes

No new permissions or dependencies (reuses OkHttp and the existing INTERNET permission). Self-reviewed against the codebase; not yet validated on a physical Android Auto head unit — feedback on real-device behaviour is welcome.

Copilot AI review requested due to automatic review settings June 17, 2026 09:47

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a built-in ad/tracker blocking feature (including cosmetic filtering and YouTube ad skipping) with user-facing settings and an auto-updating remote filter list.

Changes:

  • Introduces AdBlocker (host blocking + JS injections) and FilterListUpdater (weekly AdGuard list fetch + caching).
  • Integrates blocking into ConfiguredWebView request interception and onPageFinished script injection.
  • Adds Settings UI + preference storage + documentation updates for the new blocker.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
app/src/main/res/values/strings.xml Adds new Settings strings for Privacy/Ads and ad-block descriptions.
app/src/main/java/com/kododake/aabrowser/web/FilterListUpdater.kt New remote filter list downloader/cacher (AdGuard) with rate limiting.
app/src/main/java/com/kododake/aabrowser/web/ConfiguredWebView.kt Hooks the blocker into WebView request interception and JS injection.
app/src/main/java/com/kododake/aabrowser/web/AdBlocker.kt New ad/tracker blocking engine + cosmetic and YouTube JS.
app/src/main/java/com/kododake/aabrowser/settings/SettingsViews.kt Adds a Settings section + toggle to enable/disable ad blocking.
app/src/main/java/com/kododake/aabrowser/data/BrowserPreferences.kt Persists the ad-block enabled preference.
app/src/main/assets/adblock_hosts.txt Adds a bundled host list asset to extend the core list.
README.md Documents the new ad blocking feature and update behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/src/main/java/com/kododake/aabrowser/web/FilterListUpdater.kt
Comment thread app/src/main/java/com/kododake/aabrowser/web/FilterListUpdater.kt Outdated
Comment thread app/src/main/java/com/kododake/aabrowser/web/AdBlocker.kt
Comment thread app/src/main/assets/adblock_hosts.txt Outdated
Comment thread app/src/main/assets/adblock_hosts.txt
Comment thread app/src/main/assets/adblock_hosts.txt Outdated
@joshi-7373

Copy link
Copy Markdown
Author

Thanks for the review! I've addressed the findings in the latest commit:

Validation mutating the live blocklist before commit — fixed. Downloads are now parsed into a temporary set (AdBlocker.parse(...)) and only merged into the live blocklist (AdBlocker.addHosts(...)) after the cache file is committed, so a failed commit can no longer leave half-applied hosts in memory.

maybeUpdate running while the blocker is disabled — fixed. The remote download is now gated behind the enabled flag in both init() paths, and a refresh is triggered when the user re-enables it. This matches the README ("downloads stop entirely if you turn the ad blocker off").

yandex.ru/metrika invalid host (contains a path) — removed; mc.yandex.ru already covers Yandex Metrica.

Duplicate districtm.io — removed.

On the response.body null-guard suggestion: this project uses OkHttp 5.4, where Response.body is non-null (it was changed from nullable in OkHttp 5), so response.body.byteStream() cannot NPE here. Happy to add an explicit guard anyway if you'd prefer it for clarity.

Note: these changes are self-reviewed but have not yet been validated on a physical Android Auto head unit.

Add a built-in ad and tracker blocker for the in-app WebView:

- Network-level blocking via WebViewClient.shouldInterceptRequest using a
  merged blocklist: a hard-coded core list, a bundled hosts asset, and an
  auto-updating AdGuard DNS filter that refreshes weekly in the background.
- Conservative cosmetic CSS to hide leftover empty ad containers.
- YouTube ad handling: auto-skips skippable ads and fast-forwards
  un-skippable ones, restoring the user's mute state so real videos are
  never left muted, and only ever acting on the player's own video element.
- New 'Privacy & Ads' toggle in Settings (enabled by default).
- Robust updater: atomic cache writes, size cap, and graceful fallback to
  the bundled lists if a download fails.

Note: server-stitched (SSAI) YouTube ads cannot be removed inside a WebView.
- Validate downloaded filter lists into a temporary set and only merge
  hosts into the live blocklist after the cache is committed, so a failed
  commit can no longer leave half-applied hosts in memory.
- Gate the remote filter-list download behind the enabled flag so no
  network fetch happens while the blocker is off (matches the README), and
  refresh on enable.
- Remove an invalid host entry (yandex.ru/metrika contained a path) and a
  duplicate (districtm.io) from the bundled list.
@kododake
kododake self-requested a review July 5, 2026 06:03

@kododake kododake left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR. Introducing a native ad and tracker blocker significantly improves the browser's capabilities. The implementation is lightweight, but there are a few important points regarding thread-safety, resource management, and reliability that should be addressed before merging.

  1. Thread-Safety Concerns on Filter List Updates
    Although the list is merged into the live blocker (AdBlocker.addHosts(...)) only after successfully committing the cache file to avoid memory corruption, the operation still happens on a background thread. Since shouldInterceptRequest reads from this blocklist on the WebView's I/O thread, please ensure the underlying collections/structures in AdBlocker are thread-safe (e.g. using ConcurrentHashMap.newKeySet()) to prevent ConcurrentModificationException during concurrent reads and writes.

  2. Resource Leak: Potential Temporary File Leak on I/O Exception
    In FilterListUpdater.kt, if an IOException occurs during the execution of response.body.byteStream().use or tmp.outputStream().use, the execution will halt and jump to the caller's catch block, leaving the incomplete adblock_remote.tmp file on the disk.

  • Fix: Wrap the download and parsing logic in a try-finally block to guarantee that the temporary file is deleted:
    val tmp = File(context.filesDir, TMP_FILE_NAME)
    try {
        // download and parse logic...
    } finally {
        if (tmp.exists()) {
            tmp.delete()
        }
    }
  1. Background Thread and Lifecycle Management
    Starting a raw daemon thread via Thread { ... }.start() for network operations is discouraged in modern Android development. It ignores the system's power-saving states (Doze mode, App Standby) and has no lifecycle awareness (e.g., cancelling if the app is closed).
  • Fix: Consider refactoring this to use Android's WorkManager or Kotlin Coroutines with a network constraint (e.g., requiring network connectivity) to ensure efficient and system-friendly background scheduling.
  1. Fragility of YouTube Ad-Skipping Selectors
    The selectors used in AdBlocker.YOUTUBE_AD_SKIP_JS to fast-forward ads and auto-click skip buttons are highly dependent on YouTube's DOM structure. Since YouTube changes its DOM frequently, please ensure:
  • The injection script is strictly scoped to YouTube domains (verify that AdBlocker.isYouTube(url) uses exact domain matching to prevent injection on unrelated domains).
  • The script has robust error handling to prevent accidental fast-forwarding or permanent muting of actual user videos if selectors fail.
  1. Broken Link: FILTER_URL Returns HTTP 404
    In FilterListUpdater.kt, the defined FILTER_URL (https://raw.githubusercontent.com/AdguardTeam/AdGuardSDNSFilter/master/Filters/filter.txt) returns an HTTP 404 error (Not Found). This causes the weekly automatic filter list update to always fail.
  • Fix: Update the URL to a valid and active filter list location (e.g., checking if the repository path or branch name has changed, or using an official hosted mirror).

Please review these points and update the PR accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants