A lightweight Android app that provides a clean, ad-free Facebook feed by wrapping Facebook's mobile web client (web.facebook.com) with content filtering.
SlimBook loads Facebook's lightweight WebLite interface and automatically removes:
- Ads and sponsored posts
- Stories section
- Posts from unfollowed pages (header shows "Follow")
- Posts from unjoined groups (header shows "Join")
- Posts from non-friends (header shows "Add friend")
- "People you may know" suggestions
- "Open app" banners
- "Are you interested?" prompts
The filter runs continuously via a MutationObserver, catching new content as it arrives over Facebook's WebSocket connection.
- π‘οΈ Real-time content filtering with stats badge
- π« Removes ads, sponsored posts, stories, reels
- π₯ Removes posts from unfollowed pages and unjoined groups
- π Removes "People You May Know" and "Are you interested?" prompts
- π¬ Messenger support (via messenger.com with desktop UA)
- π€ Author management β see all detected authors, uncheck to block
- π Group management β see all detected groups, uncheck to block
- π Searchable author and group lists
- β° Post age filter (12h / 1d / 2d / 5d / 10d) β hides old posts
- π Background notification polling (15mβ12h interval)
- π· Photo upload support for posts
- π¬ Video autoplay disabled (tap to play)
- π Pull-to-refresh
- π External links open in system browser
- πͺ Persistent login (cookies survive app restarts)
- πΎ WebView state preserved when switching apps
- π Debug tools (highlight mode with filter reason labels, DOM dump, console log viewer)
- π‘ Remote filter updates β filter rules updated without new APK (disabled by default)
./gradlew assembleDebugAPK output: app/build/outputs/apk/debug/app-debug.apk
adb install app/build/outputs/apk/debug/app-debug.apkLong-press the stats badge (bottom-right corner, auto-hides while typing) to access:
- Highlight mode β shows filtered elements with red borders and reason labels (AD, PAGE, GROUP, OLD, STORIES, etc.)
- Manage authors β list of all detected post authors, uncheck to block
- Manage groups β list of all detected groups, uncheck to block
- Post age filter β hide posts older than a chosen threshold
- Notification poll β background notification checking interval (requires "Display over other apps" permission)
- Remote filter β toggle fetching filter.js from GitHub (off by default, uses bundled)
- View log β filter console messages and tracking events
- Dump DOM β logs DOM structure for debugging
- Re-run filter β manually re-apply the filter
| Permission | Purpose |
|---|---|
INTERNET |
Load Facebook |
POST_NOTIFICATIONS |
Show Android notifications for Facebook notifications |
SYSTEM_ALERT_WINDOW |
Background notification polling (invisible overlay WebView) |
- Long-press shield β "Notification poll" β select interval
- If prompted, enable "Display over other apps" in Android Settings β Apps β SlimBook
- The app polls
m.facebook.com/menu/bookmarks/in the background to check notification counts - When notifications are found, an Android notification appears; tapping it opens SlimBook to the notifications page
The messenger icon opens www.messenger.com with a desktop user agent. Press back or tap the Facebook icon to return to the feed. The "Send in Messenger" share option on posts also works, opening messenger.com with the post link pre-filled.
Facebook serves a lightweight mobile web client called WebLite at web.facebook.com. Unlike the desktop site (which uses React/Relay/GraphQL), WebLite:
- Delivers ~333KB of server-rendered HTML (vs ~3.8MB desktop)
- Streams content updates via WebSocket (not GraphQL)
- Uses only 7 JS files (vs 63+ on desktop)
- Renders posts as flat HTML elements
SlimBook injects a small JavaScript filter after each page load. The filter identifies unwanted content by text patterns ("Ad", "Join", "Follow", "Create story", etc.) and hides the parent container elements.
The content filter logic lives in filter.js at the repo root. A copy is bundled inside the APK (assets/filter.js) and used by default β no network requests are made beyond Facebook itself.
When enabled, the app downloads filter.js from this GitHub repository on each app start:
https://raw.githubusercontent.com/mretallack/SlimBook/main/filter.js
The downloaded script is then executed inside the WebView to filter Facebook content. This allows filter rules to be updated without releasing a new APK.
- Disabled (default): All communication is between your device and Facebook's servers only. The bundled filter is used.
- Enabled: The app additionally connects to GitHub (
raw.githubusercontent.com) to fetch the filter script. The script runs in the same WebView context as Facebook.
- Long-press the stats badge β "Remote filter (off)"
- A confirmation dialog explains what will happen
- Tap "Enable", then restart the app
To update filter rules without releasing a new APK:
- Edit
filter.js - Push to
main - Users with remote filter enabled will get updates on next app start
- Photo upload feedback β photos upload successfully but WebLite doesn't show a preview in the composer. A toast confirms upload.
- Follow/Join detection β uses top-60px heuristic; nested shared posts with Join/Follow are preserved but occasionally a false positive slips through.
- Background notifications β requires "Display over other apps" permission for the overlay WebView approach.
- Engagement tracking β Facebook sends binary WebSocket frames tracking viewport/scroll. See
docs/engagement-tracking.mdfor analysis.
SlimBook is designed to be published via GitHub Releases and F-Droid. To release a new version:
-
Write the Fastlane changelog first. Add
fastlane/metadata/android/en-US/changelogs/<versionCode>.txt, where<versionCode>is the new version code you are about to set, not the version name. F-Droid reads this file from the tagged commit, so it must be committed and pushed before the tag is created β otherwise the release ships with no release notes and the only way to fix it is to move the tag (see "Do not move a published tag" below). -
Update version code and version name in
app/build.gradle.kts(incrementversionCode, updateversionName). -
Commit and push changes to
main. -
Create a GitHub Release / Tag:
- Go to your repository on GitHub β Releases β Draft a new release.
- Create a new tag matching the version (e.g.,
v1.2.1). - Write release notes and publish the release.
- GitHub Actions will automatically build, sign using repository keystore secrets (
KEYSTORE_BASE64,KEYSTORE_PASSWORD,KEY_ALIAS,KEY_PASSWORD), and attachapp-release.apkto the release. - Check the Actions run went green before moving on β the
releasejob only runs ifbuild,lintandtestall pass, and a missing signing secret fails after the release is public.
-
Verify the attached APK before telling anyone the release is done:
curl -sL -o /tmp/release.apk \ https://github.com/mretallack/SlimBook/releases/download/v<version>/app-release.apk $ANDROID_HOME/build-tools/<ver>/aapt2 dump badging /tmp/release.apk | head -1 # versionCode/versionName $ANDROID_HOME/build-tools/<ver>/apksigner verify --print-certs /tmp/release.apk # must match AllowedAPKSigningKeys
-
F-Droid Metadata Update (
fdroiddata):Check for an automatic MR first (untested). The metadata sets
AutoUpdateMode: VersionwithUpdateCheckMode: Tags, which is supposed to let thecheckupdates-botdetect a new tag and open a metadata MR by itself β seefdroid/fdroiddataMRs authored bycheckupdates-bot.In practice this has not fired for SlimBook. Across
v1.2.0andv1.2.1no bot MR appeared, while the bot was actively opening them for other apps (100+ in a single month, including this author's ClayModeller and ONVIF Camera). The only SlimBook bot MR ever raised was!50330for 1.1.0, which was closed because a manual MR (!50827) superseded it. Cause unknown β SlimBook is the only one of these apps with aBinaries:line, so that interaction is a plausible candidate, but it has not been confirmed.So: look for a bot MR before authoring one by hand, but do not wait for it. If one appears and its
commit:matches the tag, merge the bot's instead of writing your own β that's less work and is the intended workflow.Worth revisiting: if the bot ever does fire reliably, this whole manual step (and the reproducibility-verification constraints that come with
Binaries:) could be dropped for routine releases.Since SlimBook uses prebuilt binaries (
Binaries:), each new release requires updating the F-Droid metadata repository (fdroiddata). Thecommit:for the new build must be the commit the release tag points at β confirm withgit rev-list -n1 v<version>rather than assuming it is the bump commit, as it may include later commits such as changelog additions.- Update local
fdroiddatarepository from upstream:cd ../fdroiddata git checkout master git fetch upstream git reset --hard upstream/master - Create a working branch:
git checkout -b update-slimbook-v1.2.1
- Edit metadata file (
metadata/uk.org.retallack.slimbook.yml): Add the new version entry underBuilds:with its corresponding release tag commit hash, and updateCurrentVersion/CurrentVersionCode. - Run F-Droid linter / rewritemeta:
fdroid rewritemetaandfdroid linttake the app id, not the file path:fdroid rewritemeta uk.org.retallack.slimbook fdroid lint uk.org.retallack.slimbook
- Commit and push:
git add metadata/uk.org.retallack.slimbook.yml git commit -m "SlimBook: update to v1.2.1" git push -u origin update-slimbook-v1.2.1 - Open a Merge Request on GitLab against
fdroid/fdroiddata. If a previous MR for the same app is still open, open the new one as a Draft until the release is final, so nothing is built from a tag you may still change.
- Update local
Deleting a tag that a published GitHub Release points at detaches the release and silently converts it back to a draft, at an untagged-β¦ URL. It is not visible in the Releases list as published any more, and the workflow is not re-triggered. Recovering means deleting the orphaned draft and creating the release again from scratch.
Tags are effectively immutable once released. If something is wrong with a release, prefer a new patch version (e.g. v1.2.2) over moving the existing tag:
- Moving the tag produces a different APK (non-reproducible builds), so any verification already done no longer applies to the artifact being shipped.
- F-Droid may already have fetched the old asset against the recorded commit, creating a mismatch between the metadata
commit:and the tag. - Reinstalling a differently-signed-but-same-key APK is fine with
adb install -r, but the artefact you tested is no longer the artefact you ship.
The only exception is a draft F-Droid MR where no build has run yet, and a GitHub release you are willing to delete and recreate.
versionCode must increase by one for every release and is what Fastlane uses for changelog filenames. Keep them in step:
versionCode |
changelog file |
|---|---|
| 1 | changelogs/1.txt |
| 4 | changelogs/4.txt |
A missing file means that version shows no release notes on F-Droid. Gaps are harmless but pointless β check the changelogs/ directory covers every version before tagging.
The release APK is signed with the release keystore; local assembleDebug builds are not. Installing one over the other fails with INSTALL_FAILED_UPDATE_INCOMPATIBLE, and the only fix is to uninstall first β which wipes app data including the WebView cookie store, i.e. the user's Facebook login.
adb install -r succeeds only when the signing key is unchanged, so release β release updates are safe and preserve the session. Prefer keeping a release build on test devices.