Clear SMS is an open-source, privacy-first SMS app for Android that automatically organizes your inbox. It categorizes messages (Important / Promotional / Personal / OTP), extracts transactions into a personal finance dashboard, surfaces bill reminders, and handles OTPs intelligently - all completely offline, on your device.
- GitHub: a signed
ClearSMS.apkis attached to every release. It runs on any device: Clear SMS itself contains no native code, and the one APK carries every ABI variant of the two small native helpers AndroidX brings in (DataStore's shared counter and Compose's path parser, ~60 KB in total), so a single universal APK covers every CPU. - F-Droid: available at f-droid.org/packages/app.clearsms. The F-Droid build is reproducible and carries the same signature as the GitHub APK, so you can install from one source and update from the other.
| Smart inbox | Finance | Alerts |
|---|---|---|
| Account detail | Extracted transaction | Search |
|---|---|---|
| Parsed notifications | OTP notification | Balance lock | Dark theme |
|---|---|---|---|
- Smart inbox - messages are automatically sorted into Important, Promotional, Personal, Unknown, and OTP using a transparent, regex-based rules engine (no ML black box).
- MMS receive & send - picture messages download automatically and can be sent from the compose bar (attach from the photo picker, camera, or any file; images are compressed to carrier limits on-device). Both directions ride the Android system's MMS service, over the carrier network only - see Privacy Principles. Image bubbles, a full-screen viewer, and attachment files stored app-privately. You can also share an image straight from your gallery into a new message. Group MMS is attributed to its sender; a dedicated group-conversation UI is not built yet, and MMS delivery reports are not supported (sent messages cap at "Sent").
- Finance dashboard - debit/credit transactions are extracted from bank SMS into accounts, credit cards, and spend summaries with hand-rolled Compose charts.
- Bills & reminders - upcoming bills and payment due dates in one Alerts view.
- OTP handling - big, copyable OTP notifications, optional auto-copy, and configurable auto-delete (24h / 3d / 7d / never).
- Scam awareness - heuristic flagging of likely scam/fraud messages.
- Material You - dynamic color on Android 12+, with a curated teal/indigo palette on older devices. Light, dark, and system themes.
- Community rules - categorization rules are plain JSON, bundled with the app and maintained by the community in this repository.
Everything shipped, and what's on the roadmap:
Messaging
- SMS send & receive (default-SMS-app role, catch-up import when the role is regained)
- MMS receive (auto-download, image bubbles, full-screen viewer, retry on failure)
- MMS send (photo picker / camera / any file, on-device compression, SIM-aware)
- Dual-SIM (per-recipient SIM memory, SIM tags on messages); the compose bar shows the slot that will send, long-press names it, and it takes the system's own SIM colour where that stays legible
- Message scheduling (long-press Send; survives reboots)
- Optional delay before sending, with a Cancel that puts the text back for editing (off by default; survives the app being killed)
- Opens
sms:,smsto:,mms:andmmsto:links from other apps, with recipient and body prefilled - Per-thread drafts with inbox preview
- Expand the compose box to fill the screen for long messages (both the standalone composer and a conversation)
- Delivery status: Sending / Sent / Delivered (real reports only) / Not sent + retry
- Share & forward selected messages; share text or images from other apps into a new message
- Per-message details (type, to/from, sent & received time, delivery time when a real report exists, failure reason)
- Tappable links, phone numbers and UPI payment links in messages (tapping a number opens the dialer; scam-flagged messages warn first)
- Undo for delete & archive (Gmail-style snackbar)
- Strip accents before sending, so one diacritic does not turn a single SMS into several (opt-in setting; applies silently, and only when it actually saves a message)
- Configurable swipe dead zone with a live translucent preview, for phones where scrolling triggered swipe actions
- Swipe-away in-app notification bars ("Message sent", schedule confirmations); swiping an UNDO bar keeps the deletion
- Recycle bin (on by default, 30-day retention, restore & delete-forever; tap a binned message to read it in full first)
- Call button in a conversation, and tap-the-name to view or create the contact (service senders explain themselves instead of doing nothing)
- Pinned conversations
- Blocked senders & blocked keywords (both go straight to the bin, silently; blocking also bins the existing conversation)
- Contact names (instead of bare numbers) in the blocked-senders list
- Group-MMS conversation UI (group messages currently attribute to their sender)
- MMS delivery reports
- Attachments persisted in drafts
- Scheduling for messages with attachments (currently SMS-only)
- Blocked keywords applied to MMS bodies
- Video compression for MMS (oversized videos are refused with a clear message; real transcoding needs MediaCodec/Media3 Transformer)
Smart inbox
- Automatic categorization: Important / Promotional / Personal / OTP / Unknown (460+ community rules + 715k sender directory)
- Automatic full re-sort after an app update ships new rules, with a progress banner in the inbox
- A rule added from a message applies to that sender's existing messages at once (body-only rules point you at the full re-sort instead)
- Category filter pills (reorderable) with tags hidden under single-category filters
- Hide whole sections you do not use (Inbox, Finance or Alerts) from Settings - the tab disappears and its notifications stop; at least one stays on
- Full-text search with category & time filters, matching contact and sender names as well as message text
- Tapping a search result opens the conversation at that message and highlights it, however old it is
- Scam-awareness flagging
- Rule manager: search, enable/disable, tap-to-edit your rules, duplicate bundled ones
- Contact suggestions while typing a recipient (compose) or a sender to block
- Alphabet fast-scroll in the contact list
Finance & alerts
- Transactions extracted into accounts, cards & wallets with spend charts
- Balance tracking with biometric balance lock
- Bills, autopay, insurance & credit-card due reminders (CRED, BOBCARD statements and undated bills included)
- Train & flight journeys in Alerts (including compact Indian Railways PNR messages)
- Deliveries with courier & tracking id
- Time-aware alerts with a complete, restorable "Older" archive
- Cross-bank UPI duplicate collapsing; retirement contributions as credits
- Conversation details screen (per-sender rename, category & finance view)
- Per-conversation custom notifications
Notifications & OTP
- Parsed transaction notifications with semantic colors and brand logos
- Big copyable OTP notifications, auto-copy, auto-delete policies, one-shot cleanup
- Always-visible Copy OTP button on OTP messages in a conversation
- Notifications clear when messages are read in-app (recycle-bin-aware actions)
- Missed-message notifications after signal loss or default-app switches
- Messages from unrecognised senders get their own notification category, on by default
- A Settings shortcut into Android's own per-category notification settings
- App-wide biometric/PIN lock (today the lock covers Finance balances)
Data & privacy
- Fully offline: no INTERNET permission (sole exception: the system's carrier MMS transaction)
- Local backup & restore for messages AND settings (timestamped files, chosen folder, scheduled)
- Settings backup with security-sensitive keys excluded by design
- Encrypted backups
- Offline by design. The app requests no INTERNET permission and makes no network calls of its own - no servers, no telemetry, no analytics. The one exception is inherent to MMS: retrieving a picture message is a transaction the Android system's MMS service performs with your carrier's MMSC over the carrier network. That transaction is how the MMS protocol works, never leaves the carrier network, and involves no third party.
- No proprietary dependencies. No Firebase, no Play Services - pure AOSP compatible.
- Your data stays on your device. Backups are local files you control.
- Transparent categorization. Every rule is human-readable JSON you can inspect, edit, export, and contribute back.
Requirements: JDK 17+ and the Android SDK (compileSdk 35).
git clone https://github.com/itsluminous/ClearSMS.git
cd ClearSMS
# point to your SDK if ANDROID_HOME is not set:
echo "sdk.dir=$HOME/Library/Android/sdk" > local.properties
./gradlew assembleDebugRun checks the same way CI does:
./gradlew ktlintCheck lintDebug testDebugUnitTest./gradlew assembleRelease produces a single universal APK under
app/build/outputs/apk/release/ (Clear SMS has no native code of its own -
the only .so files come from AndroidX's DataStore and graphics-path
helpers - so per-ABI splits would save about 45 KB and cost an extra
artifact to verify). Without signing environment variables (see
below) it is unsigned. Release APKs are shrunk with R8 and resource
shrinking but not obfuscated (-dontobfuscate in
app/proguard-rules.pro), keeping the shipped APK auditable and the build
reproducible for F-Droid verification.
Follow-up: Gradle dependency verification / lockfiles are not yet configured; CI validates the Gradle wrapper checksum but does not yet pin dependency hashes.
CI builds release APKs on every push. If signing secrets are not configured (e.g. on forks), it still succeeds and produces unsigned APKs - signed publishing activates automatically once the secrets exist.
One-time keystore generation (keep this file and its passwords private; it is
never committed - *.jks is gitignored):
keytool -genkeypair -v -keystore clearsms-release.jks -alias clearsms \
-keyalg RSA -keysize 4096 -validity 10000Then configure four repository secrets under Settings → Secrets and variables → Actions:
| Secret | Value |
|---|---|
SIGNING_KEYSTORE_BASE64 |
base64 -i clearsms-release.jks output |
SIGNING_KEYSTORE_PASSWORD |
the keystore password |
SIGNING_KEY_ALIAS |
the key alias (e.g. clearsms) |
SIGNING_KEY_PASSWORD |
the key password |
Or with the GitHub CLI:
gh secret set SIGNING_KEYSTORE_BASE64 --body "$(base64 -i clearsms-release.jks)"
gh secret set SIGNING_KEYSTORE_PASSWORD
gh secret set SIGNING_KEY_ALIAS --body "clearsms"
gh secret set SIGNING_KEY_PASSWORDPushing a tag matching v* (e.g. v0.1.0) creates a GitHub Release with the
signed ClearSMS.apk attached, with auto-generated release notes. Before
tagging, add a changelog file for the new versionCode at
fastlane/metadata/android/en-US/changelogs/<versionCode>.txt - F-Droid
shows it as the "What's New" text (see
docs/publishing-fdroid.md).
Categorization rules live under rules/ and are bundled into the APK at
build time - every app update ships the latest community rules. See docs/adding-rules.md for a step-by-step
walkthrough and CONTRIBUTING.md for the JSON schema.
Two ways to contribute:
- Pull request - add or edit a JSON file under
rules/<region>/<category>/and open a PR (use the "Rule contribution" issue template if you prefer filing an issue). - Email from the app - in the app, go to Settings → Rules → Share rules with developer. This composes an email with your exported rules JSON attached; reviewed submissions are incorporated into the next release. There are no runtime rule downloads - the app stays fully offline.
Want a fully populated app for testing or screenshots without using real
messages? Replay the synthetic demo corpus into an emulator - see
scripts/demo/.
The most useful contribution is telling us which of your messages the app fails
to categorize. scripts/audit_rule_coverage.py replays the bundled rules and the
sender-ID directory against a real SMS corpus and reports exactly that. It runs on
your computer, needs no app build, and masks all digits by default so the
output is safe to share.
1. Install the prerequisites
- Python 3.8 or newer (
python3 --version) adb, from the Android SDK platform-tools (macOS:brew install android-platform-tools)- This repository:
git clone https://github.com/itsluminous/ClearSMS.git && cd ClearSMS
2. Enable USB debugging on the phone
- Settings → About phone → Software information and tap Build number seven times to unlock Developer options
- Settings → Developer options → USB debugging → on
- Connect the phone by USB and accept the "Allow USB debugging?" prompt
- Confirm it is visible:
adb devicesshould list your device asdevice(notunauthorized)
3. Run the check
python3 scripts/audit_rule_coverage.py --from-deviceThe script reads your SMS through adb into memory only - it writes no copy of
your messages anywhere. Expect it to take a minute or two on a large inbox.
4. Read the report
- Coverage - the share of messages that got a confident category.
- Per-rule hit counts - which rules are doing the work.
- Unmatched messages - grouped by sender and body shape, ranked by how often they occur. This is the list worth reporting: the senders at the top are the biggest gaps.
generic-*rule breakdown - messages caught only by the catch-all rules, listed per sender. Generic rules are a last-resort safety net, so anything here ideally deserves a sender-specific rule.
Useful flags: --top N (how many unmatched groups to print), --generic-top N
(senders listed per generic rule), --no-generic-breakdown, and
--min-coverage N (exit non-zero below a threshold, so the audit can gate CI).
5. Share the findings
Open an issue using the Rule contribution template and paste the unmatched groups and generic breakdown sections. Before posting, read what you are about to share:
- Digits are masked as
X, but check the text anyway - names, email addresses, URLs and order references are not masked. - Never pass
--no-redacton anything you post publicly. - Do not attach a full corpus dump, and keep any corpus file outside this repository.
- Better still, send a pull request: rules are plain JSON under
rules/, and the schema is documented in CONTRIBUTING.md.
Rules must contain only generic patterns and public brand/sender names - never your account numbers, amounts or personal details.
If you would rather not use a computer at all, the app can do a simpler version of this: Settings → Rules → Share rules with developer emails your exported rules JSON, which tells us what you have had to add by hand.
Auditing from a file instead of a phone
If you already have a corpus exported as JSONL (one
{"sender": ..., "body": ...} object per line):
python3 scripts/audit_rule_coverage.py corpus.jsonl --min-coverage 80The community-maintained sender ID directory lives at
rules/sender_ids/india_sender_ids.json.gz. It is compiled into the SQLite asset the
app ships (app/src/main/assets/sender_ids.db) with:
python3 scripts/build_sender_db.py \
rules/sender_ids/india_sender_ids.json.gz \
app/src/main/assets/sender_ids.dbAfter editing the JSON, rebuild the .db and include both files in your PR.
For small fixes to wrong upstream entries (e.g. a sender ID mapped to an
unrelated business), you do not need to regenerate the large .db asset:
add the corrected entry to
rules/sender_ids/corrections.json
and copy it to app/src/main/assets/sender_id_corrections.json (a unit test
keeps the two identical). Corrections are consulted before the bundled
directory, so they always win for the same normalized sender ID.
Sender avatars for well-known brands are drawn from a curated table at
rules/brands/brands.json, bundled into the APK as
app/src/main/assets/brands.json (a unit test keeps the two copies identical -
edit the rules/brands/ master and copy it over). For brands without bundled
logo artwork (see below) the app renders an original mark from these
facts - a circular tile in the brand's published primary color, a short
monogram, and a category badge - with text color chosen by WCAG luminance so
it stays legible.
Each entry looks like:
{
"key": "hdfc",
"name": "HDFC Bank",
"category": "BANK",
"color": "#004C8F",
"monogram": "H",
"senders": ["HDFCBK", "HDFCB"],
"aliases": ["HDFC", "HDFC BANK"]
}key- unique lowercase identifier (also the bundled-logo filename key).category- one ofBANK,CARD,WALLET,TELECOM,ECOMMERCE,DELIVERY,GOVERNMENT,UTILITY,INVESTMENT,HEALTH,TRAVEL,OTHER.color- the brand's widely-published primary color as#RRGGBB.monogram- 1–3 characters drawn on the tile.senders- exact sender IDs after TRAI normalization (VM-HDFCBK→HDFCBK).aliases- whole-word names matched against resolved display names.
The APK ships real logo artwork for 27 of the curated brands under
app/src/main/assets/logos/ (~180 KB total, PNG, max 256 px). The images
are assembled by scripts/build_logo_pack.py
--bundle from the latest commits of two MIT-licensed projects
(auraveni/global-bank-logos
and cashfree/payments-icons-library);
the exact commits each build used are recorded in the manifest, so the
committed asset set stays traceable.
Per-file provenance lives in app/src/main/assets/logos/MANIFEST.md; the
upstream MIT licence texts are reproduced in NOTICE.
On the legal position: the upstream MIT licences cover those projects' packaging of the files - the logos themselves remain trademarks of the banks and merchants they identify, and are bundled solely to label message senders in your own inbox. Logos are never fetched at runtime (the app requests no network permission); brands without bundled artwork get the generated brand tiles described above.
The avatar fallback chain, in order: contact photo → bundled logo → generated brand tile → category glyph → letter avatar. All of it is gated behind Settings → Appearance → Show logos and contact photos, and every avatar renders as the same circular tile across the inbox, conversations, search, Finance and Alerts.