diff --git a/docs/store/play-data-safety.md b/docs/store/play-data-safety.md new file mode 100644 index 0000000..1fbbed8 --- /dev/null +++ b/docs/store/play-data-safety.md @@ -0,0 +1,74 @@ +# Google Play — Data Safety declaration + +This document records the answers to fill into the **Play Console → App content → +Data safety** form, with the reasoning behind each answer so it can be defended +or revisited. It reflects the app as of 2026-07-06. Update it if data handling +changes. + +See also: [`privacy-policy.md`](./privacy-policy.md). + +## Key facts the form is based on + +- The app stores everything **locally on the device** (SQLite DB + OS secure + storage + a local log file). Nothing is sent to the developer. +- The only network traffic is a **direct, user-initiated connection to the + game (MUD/MUSH) server the user typed in.** That server is a third party the + user chooses, not an endpoint we operate. +- No analytics, ads, crash-reporting SDKs, or other third-party SDKs are bundled. + +## Form answers + +### 1. Data collection and sharing + +**Does your app collect or share any of the required user data types?** → **No.** + +Rationale: In Play's model, "collected" means data transmitted off the device to +the developer or a service acting on the developer's behalf, and "shared" means +transferred to a third party. SharpClient does neither. Passwords, character +names, server addresses, transcripts, triggers, and logs are all kept in the +app's private on-device storage and are never transmitted to us. + +The character name and password **are** sent to the game server at login, but: +- the server is an endpoint **the user explicitly enters and connects to**, and +- the transfer is core, user-initiated functionality (the same category as an + SSH, FTP, email, or IRC client sending credentials to a user-specified host), +which Play's guidance does not treat as developer "collection" or "sharing." + +> ⚠️ **Confirm before submitting:** this "No data collected/shared" position +> rests on the user-directed-server interpretation above. It is the standard +> position for this class of app, but you own the final declaration. If you +> prefer maximum caution, you can instead declare *App activity / "Other +> user-generated content"* and *Personal info / "User IDs"* as **collected but +> not shared**, processed for **App functionality**, and mark it not required — +> at the cost of a more alarming store label. Recommended answer remains **No.** + +### 2. Security practices + +- **Is data encrypted in transit?** + Disclose honestly: connections to game servers use **Telnet, which is not + encrypted**. If the form scopes this question only to *collected* data (of + which there is none), it may be N/A — but the privacy policy states the + cleartext fact plainly regardless. +- **Data stored on device is protected:** saved passwords use Android + Keystore–backed secure storage; automatic cloud backup is disabled + (`allowBackup=false`). + +### 3. Data deletion + +- **Can users request that their data be deleted?** → Users delete data + themselves in-app, and uninstalling removes all local data. No server-side + data exists to delete, so no deletion request mechanism is needed. + +## Privacy policy URL (required by Play) + +The Data Safety form and the store listing both require a public **Privacy +policy URL**. `privacy-policy.md` is the content; it needs to be reachable at a +stable public URL. Options, easiest first: + +1. **GitHub Pages** — enable Pages for the repo and publish `privacy-policy.md`, + giving e.g. `https://sharpmush.github.io/SharpClient/privacy-policy`. +2. **Raw GitHub URL** — Play accepts a link to the rendered file, e.g. + `https://github.com/SharpMUSH/SharpClient/blob/master/docs/store/privacy-policy.md`. + +Pick one and paste it into both **Data safety** and **Store listing → Privacy +policy**. diff --git a/docs/store/play-feature-graphic-1024x500.png b/docs/store/play-feature-graphic-1024x500.png new file mode 100644 index 0000000..bb56b13 Binary files /dev/null and b/docs/store/play-feature-graphic-1024x500.png differ diff --git a/docs/store/play-listing-icon-512.png b/docs/store/play-listing-icon-512.png new file mode 100644 index 0000000..5ee8170 Binary files /dev/null and b/docs/store/play-listing-icon-512.png differ diff --git a/docs/store/privacy-policy.md b/docs/store/privacy-policy.md new file mode 100644 index 0000000..60eba6a --- /dev/null +++ b/docs/store/privacy-policy.md @@ -0,0 +1,75 @@ +# SharpClient — Privacy Policy + +**Effective date:** 2026-07-06 +**Applies to:** the SharpClient Android application ("the app") published by the +SharpMUSH project ("we", "us"). + +## Summary + +SharpClient is a client for connecting to text-based multiplayer games (MUD/MUSH +servers). **We do not collect or receive any of your personal data.** The app +sends what you type only to the game server you choose to connect to — never to +us or any third party. It has no analytics, no advertising, no tracking, no user +accounts with us, and no third-party SDKs. All data the app stores stays **on +your device**. + +## What the app stores on your device + +All of the following is stored only in the app's private storage on your device +and is never sent to us: + +| Data | Where it is stored | Purpose | +| --- | --- | --- | +| Game server addresses (name, host, port) | Local SQLite database (`sharpclient.db`) | So you can reconnect to games you've saved | +| Character names | Local SQLite database | To identify your login on each game | +| Character passwords / connect strings | **OS-encrypted secure storage** (Android Keystore–backed) | To auto-log-in when you choose to save them | +| Triggers and aliases you create | Local SQLite database | Your automation rules | +| Session history / game transcripts | Local SQLite database (full-text index) | So you can scroll back and search what happened | +| Diagnostic and crash logs | Local file in the app's private folder (`logs/sharpclient.log`) | So you can export logs to report a bug | + +The database and logs live in the app's private, sandboxed storage. Automatic +cloud backup is **disabled** (`allowBackup=false`), so this data is not copied to +Google Drive or any other backup service. + +## Data sent over the network + +When you connect to a game, the app opens a direct network connection **to the +server address you entered** and sends what you type — including your character +name and password at login — to **that server**. It does not send anything to us +or to any other party. + +> **Important — connections are unencrypted.** Like virtually all MUD/MUSH +> servers, connections use the Telnet protocol, which is **not encrypted**. Your +> username, password, and everything you send and receive travel in plain text +> between your device and the game server. Only connect to servers you trust, and +> avoid reusing important passwords for game characters. + +We are not the operator of the game servers you connect to. Those servers are +run by independent third parties and have their own privacy practices, which we +do not control. + +## Data sharing + +We do not share your data with anyone, because we never receive it in the first +place. + +## Children's privacy + +The app does not knowingly collect any personal information from anyone, +including children. + +## Deleting your data + +- Delete individual saved games, characters, or history from within the app. +- Uninstalling the app removes all of its local data, including the database, + saved passwords, and logs. + +## Changes to this policy + +If this policy changes, the updated version will be published at the same +location with a new effective date. + +## Contact + +Questions about this policy can be raised via the project's issue tracker: +. diff --git a/docs/store/release-checklist.md b/docs/store/release-checklist.md new file mode 100644 index 0000000..2f793e5 --- /dev/null +++ b/docs/store/release-checklist.md @@ -0,0 +1,96 @@ +# First Google Play release — checklist + +End-to-end sequence to get SharpClient onto the Play Store (internal testing +first). Ordered; each step's prerequisites are the steps above it. + +## 0. Prerequisites (one-time) + +- [ ] A Google Play Developer account (one-time US$25 registration). +- [ ] Decide the **package name is final**: `com.sharpmush.sharpclient`. This is + permanent once uploaded and can never be changed. + +## 1. Land the code changes + +- [ ] Merge the branch carrying: applicationId rename, `ForegroundScale="0.65"` + icon fix, and `docs/store/` (this checklist, privacy policy, data-safety, + 512 icon). CI (tests + Android head build) must be green. + +## 2. Create the signing keystore + configure secrets ⚠️ REQUIRED before any release + +The release workflow (`.github/workflows/release-apk.yml`) **fails fast** if the +keystore secret is absent — it will never ship an unsigned/debug build. So this +must be done before tagging. Full details in +[`../release-signing.md`](../release-signing.md). + +- [ ] Generate an upload keystore: + ```bash + keytool -genkeypair -v -keystore release.keystore \ + -alias sharpclient -keyalg RSA -keysize 2048 -validity 10000 + ``` +- [ ] Store it + passwords as repository secrets (Settings → Secrets → Actions): + `ANDROID_KEYSTORE_BASE64`, `ANDROID_KEYSTORE_PASSWORD`, `ANDROID_KEY_ALIAS`, + `ANDROID_KEY_PASSWORD`. +- [ ] **Back up the keystore + passwords somewhere safe.** With Play App Signing + (below) a lost *upload* key is recoverable; without it, losing the key means + you can't publish updates under this app identity. + +## 3. Pick the first version and tag + +`release-apk.yml` derives the Android `versionName`/`versionCode` from the release +tag (`vMAJOR.MINOR.PATCH`): +`versionCode = major*100000000 + minor*10000 + patch`. + +- [ ] Choose the version by **continuing the existing release-tag line**, not + restarting. The GitHub release tags run `v0.1 … v1.3` (v1.3 = + `versionName 1.3 / versionCode 100030000`), so the next release is **`v1.4`** + (→ versionCode `100040000`). A `v0.x` reset would derive a *lower* code and is + wrong. A Play `versionCode` can never be reused or decreased. +- [ ] Publish a **GitHub Release** with that tag. This triggers the workflow, + which builds and attaches a **signed `.aab`** (for Play) and a signed `.apk` + (for sideloading) to the release. + +## 4. Play Console — create the app + upload + +- [ ] Create the app in Play Console (default language, app name "SharpClient", + app/not game — though it's a game *client*, "App" is fine). +- [ ] Accept **Play App Signing** (recommended): Google holds the app signing + key; your keystore from step 2 is the *upload* key. +- [ ] Create an **Internal testing** release and upload the `.aab` from step 3. + Add tester email addresses. + +## 5. Play Console — required listing content + +- [ ] **App icon:** `docs/store/play-listing-icon-512.png` (512×512). +- [ ] **Feature graphic:** `docs/store/play-feature-graphic-1024x500.png` + (1024×500, required) — green M on near-black with a terminal-prompt tagline. +- [ ] **Screenshots:** at least 2 phone screenshots (existing shots live in + `.github/assets/` and may be reused/resized). +- [ ] **Short + full description.** +- [ ] **Privacy policy URL:** host `docs/store/privacy-policy.md` publicly (see + that file / the data-safety doc for options) and paste the URL. +- [ ] **Data safety form:** fill per [`play-data-safety.md`](./play-data-safety.md). +- [ ] **Content rating** questionnaire. +- [ ] **Target audience**, **ads declaration** (no ads), **news/COVID** (no). + +## 6. Submit + +- [ ] Send the internal-testing release for review; once approved, testers can + install via the opt-in link. Promote to closed/open testing → production + when ready. + +--- + +### What's already done in-repo + +- Signed `.aab` + `.apk` CI pipeline (`release-apk.yml`), tag→version derivation. +- Target API 35 (net10 android default), minSdk 24, `allowBackup=false`. +- Final package name `com.sharpmush.sharpclient`. +- Adaptive icon fixed (`ForegroundScale=0.65`) + 512 listing icon. +- Privacy policy + data-safety content. + +### Still needs a human decision / action + +- Keystore generation + secret upload (step 2). +- Version choice + tagging (step 3) — next in line is **`v1.4`**; held pending + keystore secrets + your go-ahead. +- Play Console account, listing copy, feature graphic, content rating (steps 4–6). diff --git a/src/SharpClient.App/Platforms/Android/AndroidManifest.xml b/src/SharpClient.App/Platforms/Android/AndroidManifest.xml index 489af82..d6f89ef 100644 --- a/src/SharpClient.App/Platforms/Android/AndroidManifest.xml +++ b/src/SharpClient.App/Platforms/Android/AndroidManifest.xml @@ -63,7 +63,7 @@ components. Explicit declaration required for Android 12+ (API 31). --> diff --git a/src/SharpClient.App/Platforms/Android/ConnectionKeepAliveService.cs b/src/SharpClient.App/Platforms/Android/ConnectionKeepAliveService.cs index bf8ca1e..3bd401a 100644 --- a/src/SharpClient.App/Platforms/Android/ConnectionKeepAliveService.cs +++ b/src/SharpClient.App/Platforms/Android/ConnectionKeepAliveService.cs @@ -13,12 +13,13 @@ namespace SharpClient.App; // semantically correct type. Crucially, connectedDevice has NO 6-hour time cap (unlike dataSync // which is capped at 6 h/24 h on Android 15+), which matters for sessions that last all day. // -// The Name is pinned to the real ApplicationId (com.sharpmush.sharpclient.app) so the static +// The Name is pinned to the real ApplicationId (com.sharpmush.sharpclient) so the static // AndroidManifest.xml can reference this service with a stable android:name to attach the // foregroundServiceType attribute (which cannot be set via the C# ServiceAttribute). It MUST stay // in lock-step with in SharpClient.App.csproj and the entry in the // manifest, otherwise the merger emits two distinct services and startForeground throws. -[Service(Exported = false, Name = "com.sharpmush.sharpclient.app.ConnectionKeepAliveService")] +// +[Service(Exported = false, Name = "com.sharpmush.sharpclient.ConnectionKeepAliveService")] [SupportedOSPlatform("android26.0")] // Foreground services + NotificationChannel require API 26 internal sealed class ConnectionKeepAliveService : Service { diff --git a/src/SharpClient.App/SharpClient.App.csproj b/src/SharpClient.App/SharpClient.App.csproj index 54d49ff..5c4764d 100644 --- a/src/SharpClient.App/SharpClient.App.csproj +++ b/src/SharpClient.App/SharpClient.App.csproj @@ -32,9 +32,9 @@ SharpClient - com.sharpmush.sharpclient.app + com.sharpmush.sharpclient - + 1.0 1 @@ -74,8 +74,10 @@ cleanly on it (adaptive icons, Android 8+); the teal base blends into the matching green so the background looks solid. Previously #0b0e12, which put the dark mark on near-black and rendered as "black on grey". + ForegroundScale="0.65" insets the foreground into the adaptive-icon safe zone + (MAUI renders it full-bleed otherwise, so launcher masks crop the mark). --> - +