aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-android/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-android/README.md')
-rw-r--r--packages/meshbay-android/README.md64
1 files changed, 64 insertions, 0 deletions
diff --git a/packages/meshbay-android/README.md b/packages/meshbay-android/README.md
new file mode 100644
index 0000000..85e2406
--- /dev/null
+++ b/packages/meshbay-android/README.md
@@ -0,0 +1,64 @@
+# MeshBay — Android client
+
+A client, not a host: no node runs on a phone (`docs/MESHBAY_DESIGN.md` §11.3).
+
+The shell is a system WebView showing the interface **from the package** —
+`meshbay-hub/src/meshbay_hub/static/` copied at build time into
+`build/generated/`, never committed (§8.3) — with a bridge
+(`app/src/main/assets/bridge/meshbay-bridge.js`) that offers the page the same
+`window.meshbay` as the desktop preload, wherever it offers anything at all.
+Hub calls leave from native code, to the signed-in hub only. The device key,
+the bundle key and every node identity are held natively under an Android
+Keystore key; the page is told public keys and handed signatures, asked for by
+kind — never bytes. `meshbay-hub/tests/vectors/keyring.json` holds that keyring
+to the desktop's and to the specification.
+
+Downloads are written to disk as they arrive — into the folder chosen in
+Settings (a Storage Access Framework tree, as `<name>.part` until complete) or
+the system Downloads collection (a pending entry until complete) — and the
+page holds an opaque id, never a URI. Uploads come through the system picker.
+
+Casting: the page pushes the decrypted stream to a local HTTP relay (a port of
+the desktop's `cast-relay.js`, bound to the Wi-Fi address only); receivers are
+found and driven through the platform cast SDK with the default media receiver.
+While a cast runs, a media-playback foreground service holds the CPU and the
+Wi-Fi, and the WebView is kept reported visible — without that, Chromium
+freezes the page 60 s after the screen goes off. Where play services are
+absent, the page is offered no cast at all.
+
+```bash
+# needs JDK 17+ and an Android SDK (ANDROID_HOME, or sdk.dir in local.properties)
+./gradlew assembleDebug # app/build/outputs/apk/debug/app-debug.apk
+./gradlew testDebugUnitTest # JVM unit tests
+```
+
+The security contract is also pinned from the Python suite by reading this
+source: `packages/meshbay-hub/tests/test_android_shell.py`.
+
+Not built yet: phone-specific behaviour (back button, network handover,
+keeping a download alive with the screen off), signed releases.
+
+## Icon
+
+The desktop client's icon, `meshbay-client/build/icon-square.png`, placed in
+the adaptive icon's safe zone (72 dp of the 108 dp layer, which is what every
+launcher mask leaves visible) over its own edge colour, so no mask crops the
+M. The five `mipmap-*/ic_launcher_foreground.png` are generated from it:
+
+```python
+from PIL import Image, ImageDraw, ImageFilter
+src = Image.open("../meshbay-client/build/icon-square.png").convert("RGBA")
+for name, L in [("mdpi", 108), ("hdpi", 162), ("xhdpi", 216), ("xxhdpi", 324), ("xxxhdpi", 432)]:
+ S = L * 72 // 108; b = max(2, S // 25)
+ img = src.resize((S, S), Image.LANCZOS)
+ mask = Image.new("L", (S, S), 0)
+ ImageDraw.Draw(mask).rectangle([b, b, S - b - 1, S - b - 1], fill=255)
+ img.putalpha(mask.filter(ImageFilter.GaussianBlur(b)))
+ layer = Image.new("RGBA", (L, L), (0, 0, 0, 0))
+ layer.paste(img, ((L - S) // 2, (L - S) // 2), img)
+ layer.save(f"app/src/main/res/mipmap-{name}/ic_launcher_foreground.png", optimize=True)
+```
+
+No monochrome layer: a themed icon keeps only the layer's alpha, and this one
+would be a filled square.
+