# Play Store submission

Answers for the content declarations, each traceable to something in the
code rather than assumed. A wrong answer here gets an app removed, so
where something is uncertain this says so.

Verified against `versionCode 2`.

---

## The build

| | |
|---|---|
| Package | `com.onairadar.app` |
| versionCode / versionName | `2` / `0.1.0` |
| minSdk / targetSdk | 24 (Android 7.0) / 36 |
| Signing | Release keystore, `CN=On AI Radar`, valid to 2054 |
| SHA-256 | `A9:CC:2A:F5:F6:A2:8B:0B:A9:1F:3F:53:18:32:8A:3E:6C:B5:C2:60:9F:87:7C:9C:85:43:40:80:74:D2:E9:FE` |
| `android:debuggable` | Not set |

**The keystore is at `apps/mobile/credentials/` and is gitignored.** It is
the only thing that can ever update this app — Google cannot recover it.
Back it up somewhere off this machine.

Note: `expo prebuild --clean` deletes `android/keystore.properties`.
Keeping the real keystore in `credentials/` rather than inside `android/`
is what makes that survivable. Restore with:

```bash
cp apps/mobile/credentials/keystore.properties apps/mobile/android/
```

---

## Declarations

### Advertising ID — **No**

`com.google.android.gms.permission.AD_ID` is not declared, and there is
no ad or attribution SDK in the build. Verified by searching the merged
manifest inside the AAB: zero occurrences.

### Ads — **No ads**

No AdMob, no ad network, no sponsored placements in the app. (The
*website* has ad slots, currently disabled. The app has none.)

### Content rating — news / information

- No user-generated content
- No user-to-user communication
- No ads
- No purchases, no gambling, no violence
- Stories link out to third-party news sources

Content is AI industry news. Expect **Everyone** or **Teen** depending on
how the questionnaire treats outbound news links.

### Target audience — **13+ or 18+. Not child-directed.**

An AI news reader for developers and founders. Nothing in the design,
copy, or content appeals to children.

### Government app — **No**
### Financial features — **No**

No payments, no purchases, no banking, no crypto, no lending.

### Health — **No**

No health data of any kind.

---

## Data safety

### Collected and sent off-device

| Data | When | Purpose | Shared? | Optional? |
|---|---|---|---|---|
| Email address | Only if the reader creates an account | Account management | No | **Yes** — the app works signed out |
| Password | Only at sign-up / sign-in | Authentication | No | **Yes** |

**Both are optional.** Reading requires no account; signing in only syncs
filters and saved stories with the website.

⚠️ **Do not declare "Name".** The app never asks for one. `displayName`
exists on the `Account` type in `src/lib/auth.tsx` but is never read,
displayed, or sent — registration posts `email`, `password` and `locale`
only. Declaring data you don't collect is itself a Data Safety violation.

### Stored only on the device, never transmitted

Locale, theme, saved stories, session token. AsyncStorage keys:
`airadar.locale`, `airadar.theme`, `airadar.saved`, `airadar.session`.

### Not collected at all

Location, contacts, photos, files, camera, microphone, calendar, SMS,
call logs, advertising ID, behavioural analytics, crash logs, health,
financial data.

There is **no third-party analytics or crash SDK** in the build — no
Firebase Analytics, Crashlytics, Sentry, Facebook, AppsFlyer, Mixpanel,
Segment or Amplitude.

### Answers to Google's specific questions

- **Encrypted in transit?** Yes. All production traffic is HTTPS to
  `onairadar.com`. The only `http://` in the source is a `localhost`
  development fallback that cannot be reached from a device.
- **Can users request deletion?** **Yes.** In-app: Settings → Delete
  account, with a confirmation dialog. On the web:
  `https://onairadar.com/en/delete-account`.
- **Data sold or shared with third parties?** No.

### About push notifications

The notifications toggle is **hidden in this release** — see below — so
no push token is collected. When it is re-enabled, the declaration gains:
a push token and the device platform, sent to the app's own server and to
Expo's push service, for app functionality, not shared, and optional.

---

## Sign-in details for review

Login is **optional**. Reviewers must be able to confirm the app works
fully signed out — that is the normal path.

A reviewer account is already registered on production. The credentials
are in `apps/mobile/credentials/play-review-account.txt`, which is
gitignored — deliberately not committed, so back it up with the keystore.

**Do not delete that account.** Google reuses the same credentials to
review every future update, so removing it gets the next release
rejected.

It is deliberately left **unverified**, and that is fine: `login()` in
`apps/api/src/auth/auth.service.ts` returns a valid session token
regardless of `email_verified` — `needsVerification` is reported to the
client but never enforced. A reviewer never needs the mailbox. Verified
against production: register → login → `GET /auth/me` all return 200.

Two limits a reviewer could trip, both temporary and self-clearing:
8 wrong passwords locks the account for 15 minutes, and login is capped
at 10 attempts per 15 minutes per IP.

Add a review note:

> Signing in is optional. The app is fully usable without an account —
> all stories, sections and saved items work signed out. An account only
> syncs alert filters and saved stories with the website.

No social login in the app (email and password only). Google, GitHub and
Apple sign-in exist on the website but are not wired into the app.

---

## Privacy policy

```
https://onairadar.com/en/privacy
```

Bilingual, English and Arabic (`/ar/privacy`). Linked from Settings.

⚠️ **Must return 200 before submitting.** The page is committed but
requires `bash deploy/deploy.sh` on the server. Check it in a browser
first — Play rejects a listing with a dead privacy URL.

---

## Store assets

| Asset | File | Size |
|---|---|---|
| Icon | `apps/mobile/assets/google-play-icon-512.png` | 512×512 |
| Feature graphic | `apps/mobile/store-assets/feature-graphic-1024x500.png` | 1024×500 |
| Screenshots | `apps/mobile/store-assets/screenshots/0*-play.png` | 1080×1920 (4) |

The screenshots are captures of the real app running on an emulator with live
content — not mockups. The device is 1080×2400, so each one is scaled down to
fit 1080×1920 and padded with white rather than cropped or stretched. Cropping
lost the tab bar and cut text mid-sentence; stretching would have distorted it.
The pad matches the app background, so it is not visible.

---

## Push notifications are hidden in this release

`PUSH_ENABLED = false` in `src/screens/SettingsScreen.tsx`.

Expo push tokens need an EAS project id, issued by Expo when you run
`eas init`. Without one, `getExpoPushTokenAsync` throws in a standalone
build and the toggle fails with "could not turn notifications on". A
control that silently fails is worse than no control, and a reviewer
trying it would see a broken feature.

The push code is complete and tested — only the UI is hidden. To restore:

1. `cd apps/mobile && npx eas-cli login && npx eas-cli init`
2. Put the id in `app.json` under `expo.extra.eas.projectId`
3. Set `PUSH_ENABLED = true`
4. Rebuild with `versionCode` 3
5. Update the Data Safety form to declare the push token

---

## Before you submit

- [ ] `deploy.sh` run — `/en/privacy` and `/en/delete-account` return 200
- [ ] Keystore backed up off this machine
- [ ] Test account created for reviewers
- [ ] Screenshots, feature graphic and icon uploaded
- [ ] All ten declarations answered as above
