Move off the reserved com.example placeholder namespace to the app's own package identifier.
164 lines
No EOL
5.8 KiB
Markdown
164 lines
No EOL
5.8 KiB
Markdown
# 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 (`5–300`, default **30**), then
|
||
the previous volume is restored.
|
||
|
||
### Volume
|
||
The ring plays at a configurable percentage (`1–100%`) 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. |