The packages now carry the manual, but nothing in the application opened
it: the help sheet listed gestures and stopped there.
The help sheet gains a Manual button beside Done, and Settings a Manual
row under About beside the version. Both go through dr_ui::manual, which
finds the installed page through dr_plat::system_data_dirs (the package's
share directory on Linux, the executable's directory on Windows), and a
development build also in the checkout it was compiled from. A copy with
no manual says so on the status line rather than doing nothing.
On the desktop the page goes to the system browser. A section is a URL
fragment, and xdg-open's generic mode and Windows' FileProtocolHandler
both turn a file: URL into a path and drop the fragment, so a section is
opened through a one-line redirect page written to the data directory:
the opener gets a plain path, which every opener keeps, and the browser
follows the redirect to index.html#section itself. The launcher behind
the sign-in's open_in_browser is split out so both share it; the https
check stays with the sign-in.
Android has no path to give a browser: an asset is not a file, a copy in
private storage is unreadable to other apps, a file: URI across apps is
refused, and a content: URI leaves the browser resolving every picture
against the provider. So ManualActivity, a WebView reading
file:///android_asset/manual/index.html straight out of the APK, shows
it, started by class name with the section as an extra. JavaScript is
off, links off the page go to the browser, and the theme is day-night so
the page's own light and dark follow the system. A test checks that the
manifest, the Java class and dr_ui agree on the name and the extra.
There was no icon anywhere, and on Android that was not a missing line in the
manifest. `aapt2 link` was being handed a manifest and nothing else, so the
APK carried no res/ and no resources.arsc — there was no table for an
`@mipmap/...` reference to resolve against even if one had been written.
Packaging now compiles the resource tree first and links the result in, which
is the two steps aapt2 insists on: link reads compiled input only, never a
directory.
That absent table is also why the launcher caption was blank, which had
looked like a second, separate bug. `android:label="DarkRoom"` was there and
correct the whole time, and Settings' App info read it fine; the launcher
could not, because resolving a label goes through the package's Resources and
there were none to open. Nothing about the label changed here. It came back
with the table under it.
`android:icon` then names one drawable for both icon generations, because the
`anydpi-v26` qualifier is what separates them. API 26 and up take the adaptive
icon and its three layers; the third of those, monochrome, is what lets
Android 13 recolour it rather than drop the app out of the themed set. Below
26 the same name lands on a density-matched PNG. `roundIcon` is deliberately
absent — a launcher old enough to read it is one that would ignore the
adaptive XML, and minSdk is 28.
The desktop icon is one `@image-url` on the window, and the only raster asset
in a UI that is otherwise entirely Path. The reasoning at the top of
icons.slint does not reach it: that is about glyphs a font might not carry,
and this image is never drawn by us at all. It goes to the window manager,
which wants pixels and composites them unmasked, so it is pre-shaped with
rounded corners rather than square the way the Android layers are.
Which exposed Slint's resource default. An `@image-url` compiles down to the
absolute path it had on the build machine, to be opened at runtime — already
wrong for Android, where the build happens under /work inside a container and
no such directory exists on the device, and wrong silently, as an image that
loads empty. `EmbedFiles` puts the bytes in the binary instead. It reaches
nothing else, since every glyph is a Path.
Verified on a device: the APK installs and the home screen draws both the
icon and "DarkRoom" under it, where before it had neither. In the link step
the adaptive icon resolves at all six densities and resources.arsc lands
uncompressed, which API 30 requires and the existing zipalign preserves. On
the desktop by reading _NET_WM_ICON off the running window — 256x256, as
handed over. Where that actually shows is narrower than it sounds, and the
comment says so: Wayland ignores the property in favour of matching app_id
against an installed .desktop file, which this repo does not install.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>