summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_android_shell.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-17 13:29:25 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-17 13:29:25 +0200
commit165847da1ca3c3be627b5968bc77e9a11b3385cb (patch)
tree603329a801d1304fdba833c1a8f4cd8c4ce4e7c6 /packages/meshbay-hub/tests/test_android_shell.py
parenta1d18a2957d167444c6fc6c2e4ec8628509a7009 (diff)
downloadmeshbay-165847da1ca3c3be627b5968bc77e9a11b3385cb.tar.gz
feat(android): ask for the local network when a group opens
Android 17 makes it a permission for an application targeting SDK 37. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/tests/test_android_shell.py')
-rw-r--r--packages/meshbay-hub/tests/test_android_shell.py33
1 files changed, 30 insertions, 3 deletions
diff --git a/packages/meshbay-hub/tests/test_android_shell.py b/packages/meshbay-hub/tests/test_android_shell.py
index 520fa7e7..cba4b320 100644
--- a/packages/meshbay-hub/tests/test_android_shell.py
+++ b/packages/meshbay-hub/tests/test_android_shell.py
@@ -137,7 +137,8 @@ def test_the_shim_offers_desktop_channels_and_native_answers_each():
native = set()
for path in (SRC / "bridge" / "Channels.kt", SRC / "bridge" / "KeyChannels.kt",
SRC / "cast" / "CastChannels.kt", SRC / "notify" / "PushChannels.kt",
- SRC / "photos" / "PhotoChannels.kt", SRC / "phonesync" / "Destination.kt"):
+ SRC / "photos" / "PhotoChannels.kt", SRC / "phonesync" / "Destination.kt",
+ SRC / "shell" / "LocalNetwork.kt"):
native |= set(re.findall(r'^\s*"([\w:-]+)" ->', _read(path), flags=re.M))
# Contacts and the calendar share one class, its channels named by a prefix.
docs = set(re.findall(r'^\s*"([\w-]+)" ->', _read(SRC / "phonesync" / "DocChannels.kt"), flags=re.M))
@@ -151,10 +152,11 @@ def test_the_shim_offers_desktop_channels_and_native_answers_each():
assert shim, "no channel found in the shim"
# A phone has a push distributor to talk to and a camera roll and an
# address book to back up, and a desktop has none of them; those families are the ones the desktop lacks
- # rather than the ones it shares.
+ # rather than the ones it shares. Nor does a desktop ask before reaching the
+ # local network (Android 17).
phone_only = {c for c in shim if c.startswith(("push:", "photosync:", "phonesync:", "contactsync:",
"calendarsync:", "drivesync:",
- "wasync:"))}
+ "wasync:", "lan:"))}
assert any(c.startswith("push:") for c in phone_only), "the push channels are gone"
assert any(c.startswith("photosync:") for c in phone_only), "the backup channels are gone"
assert shim - phone_only <= preload, f"channels the desktop does not have: {shim - preload}"
@@ -236,6 +238,31 @@ def test_a_notification_is_drawn_only_when_it_opened_and_leads_inside_the_page()
assert 'location.hash = ${JSONObject.quote(link)}' in activity
+def test_the_local_network_is_asked_for_when_a_group_opens():
+ """Android 17 makes the local network a permission for an application that
+ targets it, and the node is usually on the phone's Wi-Fi: without it the
+ group opens on mobile data and not at home. The page's WebRTC carries this
+ application's permission. Asked when a group is first opened, never at
+ start, and only by an application that targets 37: before that the
+ permission comes with INTERNET and asking for it is what Android says not
+ to do."""
+ manifest = _read(APP / "src" / "main" / "AndroidManifest.xml")
+ assert "android.permission.ACCESS_LOCAL_NETWORK" in manifest
+ target = int(re.search(r"targetSdk = (\d+)", _read(APP / "build.gradle.kts")).group(1))
+ assert target >= 37, "the permission is declared by an application that does not target 37"
+ activity = _read(SRC / "MainActivity.kt")
+ on_create = activity.split("override fun onCreate(", 1)[1].split("\n override fun ", 1)[0]
+ assert "requestPermissions" not in on_create
+ assert "lan.deliverPermission(requestCode)" in activity
+ lan = _read(SRC / "shell" / "LocalNetwork.kt")
+ assert "ACCESS_LOCAL_NETWORK" in lan and "ACTION_APPLICATION_DETAILS_SETTINGS" in lan
+ from spa_source import STATIC
+ page = _read(STATIC / "group-page.js")
+ asked = page.split("platform.localNetwork.available && !lanAsked", 1)
+ assert len(asked) == 2 and "platform.localNetwork.request()" in asked[1][:200]
+ assert "lanAsked = true" in asked[1][:200]
+
+
def test_no_backup_carries_the_keys_away():
manifest = _read(APP / "src" / "main" / "AndroidManifest.xml")
assert 'android:allowBackup="false"' in manifest