lost-my-phone/README.md
avi 6b6f242edb refactor: rename package to lostmyphone.app
Move off the reserved com.example placeholder namespace to the app's own
package identifier.
2026-08-14 16:19:54 -05:00

164 lines
No EOL
5.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Lost My Phone
An Android app that listens for SMS commands to trigger a loud ring — overriding
"Do Not Disturb" (DND) — so you can find your phone.
- **Package:** `lostmyphone.app`
- **No Google Play Services.** Pure Android SDK.
- **Languages:** Kotlin, Material Components (AndroidX).
---
## SMS Trigger
The app listens for a **configurable trigger phrase**. When an incoming SMS body
matches it exactly (case-insensitive, trimmed), the app rings.
> Both the phrase, the sound it plays, and the duration are configurable from the
> in-app **Ring Settings** screen (launched from the **"Ring Settings"** button).
> Send the configured phrase to your phone from any other phone to trigger it.
### Trigger Phrase (default: `RING_NOW`)
Configured in **Settings**. Any SMS whose body equals the phrase triggers the ring.
### Sound
Choose between:
- **System ringtone** (or pick a specific one), or
- **Custom song** — pick any audio file on the device.
### Duration
Playback runs for the configured number of seconds (`5300`, default **30**), then
the previous volume is restored.
### Volume
The ring plays at a configurable percentage (`1100%`) of the alarm-stream volume.
---
## How Do Not Disturb Override Works (read this)
Android does **not** allow any app to silently bypass DND. The only supported path
is the **"Do Not Disturb access"** special app setting. **Without it the app will
refuse to play any sound.** This is enforced at two points:
1. **`SmsReceiver`** passes whether access is granted to the service.
2. **`RingService.onStartCommand`** re-verifies
`NotificationManager.isNotificationPolicyAccessGranted` as the **source of
truth**. If it is `false` the service:
- does **not** start playback,
- shows an ongoing "Action Required: DND Access" notification,
- and launches the system DND-access settings screen.
Only after the user flips that toggle will a trigger actually ring.
When access **is** granted, `RingService`:
- saves the current **alarm-stream** volume,
- sets `AudioManager.STREAM_ALARM` to the configured volume percentage,
- requests `AUDIOFOCUS_GAIN_TRANSIENT` using `AudioAttributes.USAGE_ALARM`,
- plays on the **alarm stream** — the one stream Do Not Disturb does **not**
silence by default (media/music *is* silenced), which is what lets the ring
cut through DND,
- plays for the configured number of seconds, then restores the volume and
abandons focus.
---
## Build & Install
### Prerequisites
- JDK 17
- Android SDK with `compileSdk = 34` (platform 34 + build tools) — see
`app/build.gradle.kts`.
### Steps
```bash
cd /path/to/Lost_My_Phone
# 1) Point Gradle at your Android SDK (if not already in $ANDROID_HOME)
# create a local.properties file:
# echo "sdk.dir=/path/to/Android/Sdk" > local.properties
# 2) Generate the Gradle wrapper (optional if you already have gradle 8.6)
gradle wrapper
# 3) Build the debug APK
./gradlew :app:assembleDebug
# 4) The APK is at app/build/outputs/apk/debug/app-debug.apk
# 5) Install with adb
adb install app/build/outputs/apk/debug/app-debug.apk
```
---
## First-Launch Permission Setup
On install you must grant **four** things, in this order:
### 1. SMS permission (required — SMS receiver will not fire without it)
Launch the app → tap **"Grant SMS & Media Permissions"** → **Allow**.
- On Android 13+ this also asks for `READ_MEDIA_AUDIO` (needed for `PLAY_SONG`).
### 2. Notification permission (Android 13+)
Tap **"Grant Notification Permission"** → **Allow**.
(Needed so the foreground-service notification shows.)
### 3. Do Not Disturb Access (CRITICAL)
The only way the app can legally override DND. Do **one** of:
- **(A)** Tap **"Open Do Not Disturb Access Settings"** in the app, **or**
- **(B)** Navigate manually:
```
Settings > Apps > (All / See all N apps) > Lost My Phone
> Special access > "Do Not Disturb access" > ALLOW / turn ON
```
> Some OEMs label it "Do Not Disturb access", some "Notification policy access".
> Older Android versions: `Settings > Apps > Advanced > Special app access`.
> Exact wording varies by manufacturer/Android version; search Settings for
> **"Do Not Disturb access"**.
Once enabled, return to the app — the status card should read **"Do Not Disturb
Access: GRANTED"**.
### 4. (Optional) Manual test
Tap **"Test Ring Now (30s)"**. Must have completed step 3 or it will warn and open
settings instead.
---
## Architecture
```
app/src/main/java/lostmyphone/app/
├── MainActivity.kt # UI: status, permission requests, DND-access launcher, manual test
├── SmsReceiver.kt # BroadcastReceiver: parses SMS -> detects RING_NOW / PLAY_SONG
└── RingService.kt # ForegroundService: DND gate, volume override, focus + playback, 30s timer
```
- **`SmsReceiver`** listens for `android.provider.Telephony.SMS_RECEIVED` and starts
`RingService` as a foreground service (`foregroundServiceType="mediaPlayback"`).
- **`RingService`** encapsulates all playback and DND logic so the ring continues
with the screen off.
---
## Permissions declared (`AndroidManifest.xml`)
- `ACCESS_NOTIFICATION_POLICY` — enables DND override once user grants special access.
- `RECEIVE_SMS` — to detect the trigger commands.
- `FOREGROUND_SERVICE` + `FOREGROUND_SERVICE_MEDIA_PLAYBACK` — media foreground service.
- `READ_MEDIA_AUDIO` (13+) / `READ_EXTERNAL_STORAGE` (≤ 12) — MediaStore song search.
- `POST_NOTIFICATIONS` (13+) — foreground / status notifications.
- `WAKE_LOCK`, `VIBRATE` — ensure the ring is audible/prominent.
---
## Privacy & Safety
- The app **never** plays audio unless (a) the user has granted DND access **and**
(b) a valid trigger was sent or the manual test was tapped.
- Permit to Media and SMS are only used for the described purpose. No data leaves
the device, no analytics, no network calls, no GMS/Firebase.