Android was the one app that could open a punktfunk:// link but never hand one out. So every Android link had to be typed by hand — and the host's stable record id, the part that keeps a link working after the box changes address or the client is reinstalled, isn't shown anywhere in the UI to type.
This came out of a user asking how to wire the Android app into Beacon Launcher. Nothing was actually missing for that — MainActivity is exported and handles ACTION_VIEW for the scheme, and launch= is wired end to end, so Beacon's custom-launch intent (am start -a android.intent.action.VIEW -d "punktfunk://connect/<host>?launch=steam:570") works today. What was missing was any way to get the URL off the device without hand-assembling it.
What changed
Copy link in both homes, which matters here specifically: a handheld with a controller attached is in the console home, so a touch-only action would have missed exactly the user who asked.
Touch grid — a new item in the host card's overflow menu
Console home — a new action in the host options dialog (Up on a tile), including on pinned tiles
The URL comes from DeepLinks.forHost(), the shared self-emitted form that was already sitting unused in the kit. So Android now emits what every other client emits: stable record id first, with host= and fp= alongside as the recovery path. A pinned card carries its own profile, matching Linux and Apple; a plain host card carries none and keeps honouring the host's binding, exactly like tapping it does.
Confirmation UX
Android 13+ draws its own clipboard preview, so we add nothing on top of it — stacking a second confirmation is the platform's own documented anti-pattern. Below 13 we say so ourselves: the notice banner in the touch grid, a toast in the console home, which renders neither banner.
Docs
profiles-and-links.md no longer says "the Android app has no copy action yet", and its pinned-card paragraph now reads "Linux, Apple and Android". Checked and correctly left alone: clients.md's generic pointer, and the support matrix's punktfunk:// column (already ✅ for Android — that column is about links working, which they always did; only emission was missing).
No release-notes entry: there's no v0.25.0.md on main, and those notes are prepared elsewhere — this needs picking up there.
Verification
:app:testDebugUnitTest (compiles app + kit) and :kit:testDebugUnitTest both green. :app:assembleDebug not run — it pulls in the full Rust NDK build and this is Kotlin-only, so compileDebugKotlin is the meaningful gate. The emitted URL itself is already covered by the cross-language vector tests, so the three emitters can't drift.
Not yet exercised on glass. Worth a tap on a real device, particularly the sub-Android-13 toast path.
Android was the one app that could **open** a `punktfunk://` link but never **hand one out**. So every Android link had to be typed by hand — and the host's stable record id, the part that keeps a link working after the box changes address or the client is reinstalled, isn't shown anywhere in the UI to type.
This came out of a user asking how to wire the Android app into [Beacon Launcher](https://play.google.com/store/apps/details?id=com.radikal.gamelauncher). Nothing was actually missing for that — `MainActivity` is exported and handles `ACTION_VIEW` for the scheme, and `launch=` is wired end to end, so Beacon's custom-launch intent (`am start -a android.intent.action.VIEW -d "punktfunk://connect/<host>?launch=steam:570"`) works today. What was missing was any way to get the URL off the device without hand-assembling it.
## What changed
**Copy link in both homes**, which matters here specifically: a handheld with a controller attached is in the console home, so a touch-only action would have missed exactly the user who asked.
- Touch grid — a new item in the host card's overflow menu
- Console home — a new action in the host options dialog (Up on a tile), including on pinned tiles
The URL comes from `DeepLinks.forHost()`, the shared self-emitted form that was already sitting unused in the kit. So Android now emits what every other client emits: stable record id first, with `host=` and `fp=` alongside as the recovery path. A **pinned card carries its own profile**, matching Linux and Apple; a plain host card carries none and keeps honouring the host's binding, exactly like tapping it does.
## Confirmation UX
Android 13+ draws its own clipboard preview, so we add nothing on top of it — stacking a second confirmation is the platform's own documented anti-pattern. Below 13 we say so ourselves: the notice banner in the touch grid, a toast in the console home, which renders neither banner.
## Docs
`profiles-and-links.md` no longer says "the Android app has no copy action yet", and its pinned-card paragraph now reads "Linux, Apple and Android". Checked and correctly left alone: `clients.md`'s generic pointer, and the support matrix's `punktfunk://` column (already ✅ for Android — that column is about links *working*, which they always did; only emission was missing).
No release-notes entry: there's no `v0.25.0.md` on `main`, and those notes are prepared elsewhere — this needs picking up there.
## Verification
`:app:testDebugUnitTest` (compiles app + kit) and `:kit:testDebugUnitTest` both green. `:app:assembleDebug` not run — it pulls in the full Rust NDK build and this is Kotlin-only, so `compileDebugKotlin` is the meaningful gate. The emitted URL itself is already covered by the cross-language vector tests, so the three emitters can't drift.
**Not yet exercised on glass.** Worth a tap on a real device, particularly the sub-Android-13 toast path.
Android was the one app that could open a punktfunk:// link but never hand
one out, so every Android link had to be typed by hand — and the host's
stable record id, which is the part that keeps a link working after the box
changes address, isn't shown anywhere in the UI to type.
Both homes now offer Copy link: the touch grid's card overflow menu, and the
controller home's host options (Up on a tile). A pinned card copies its own
profile with it, matching Linux and Apple; a host card copies none and so
keeps honouring the host's binding, exactly like tapping it does.
The URL is the shared self-emitted form (DeepLinks.forHost), already covered
by the cross-language vector tests, so the three emitters stay in step.
Android 13+ draws its own clipboard confirmation and we add nothing on top of
it; below that we say so ourselves, as a toast in the console home, which
renders neither banner.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Android was the one app that could open a
punktfunk://link but never hand one out. So every Android link had to be typed by hand — and the host's stable record id, the part that keeps a link working after the box changes address or the client is reinstalled, isn't shown anywhere in the UI to type.This came out of a user asking how to wire the Android app into Beacon Launcher. Nothing was actually missing for that —
MainActivityis exported and handlesACTION_VIEWfor the scheme, andlaunch=is wired end to end, so Beacon's custom-launch intent (am start -a android.intent.action.VIEW -d "punktfunk://connect/<host>?launch=steam:570") works today. What was missing was any way to get the URL off the device without hand-assembling it.What changed
Copy link in both homes, which matters here specifically: a handheld with a controller attached is in the console home, so a touch-only action would have missed exactly the user who asked.
The URL comes from
DeepLinks.forHost(), the shared self-emitted form that was already sitting unused in the kit. So Android now emits what every other client emits: stable record id first, withhost=andfp=alongside as the recovery path. A pinned card carries its own profile, matching Linux and Apple; a plain host card carries none and keeps honouring the host's binding, exactly like tapping it does.Confirmation UX
Android 13+ draws its own clipboard preview, so we add nothing on top of it — stacking a second confirmation is the platform's own documented anti-pattern. Below 13 we say so ourselves: the notice banner in the touch grid, a toast in the console home, which renders neither banner.
Docs
profiles-and-links.mdno longer says "the Android app has no copy action yet", and its pinned-card paragraph now reads "Linux, Apple and Android". Checked and correctly left alone:clients.md's generic pointer, and the support matrix'spunktfunk://column (already ✅ for Android — that column is about links working, which they always did; only emission was missing).No release-notes entry: there's no
v0.25.0.mdonmain, and those notes are prepared elsewhere — this needs picking up there.Verification
:app:testDebugUnitTest(compiles app + kit) and:kit:testDebugUnitTestboth green.:app:assembleDebugnot run — it pulls in the full Rust NDK build and this is Kotlin-only, socompileDebugKotlinis the meaningful gate. The emitted URL itself is already covered by the cross-language vector tests, so the three emitters can't drift.Not yet exercised on glass. Worth a tap on a real device, particularly the sub-Android-13 toast path.