android: the launcher's ground is the desktop's, not a flat colour under it
The icon round left the adaptive background as one `#FF05080B` and a note that a gradient was being mocked. The mock settles it: the falcon covers about a quarter of a circle-masked tile, so the ground is three quarters of what anyone sees, and concept 04's glow survives every mask — the visible top-left corner goes from `#05080B` to `#0E3543`. So `@color/ic_launcher_background` becomes `@drawable/ic_launcher_ground`, a vector carrying the desktop icon's own linear surface and the radial glow over it, every number `src-tauri/icons/icon.svg`'s multiplied by 108/1024 and nothing else. The colour resource stays where it is, unreferenced, because its filename is the only thing stopping tauri's own `#fff` from colliding with it. A vector and not five PNGs on evidence. The kit's glow has a uniform scale, so the SVG ellipse is a circle and its rotation is a no-op, which is exactly what `android:type="radial"` can carry; two numbers there and no vector could have said it. The linear axis is 51.1° off horizontal, which rules out a `<layer-list>` of shapes as well, whose angle must be a multiple of 45. It was compiled and linked with the SDK's own aapt2 over the real merged res tree and every stop read back out of the APK. The name is `ic_launcher_ground` and not `ic_launcher_background` on purpose: the generated project still carries Android Studio's template drawable of that name, so under the other name an overlay copy that failed would silently link the green robot — under this one it is a hard link error, which was tested too. `verify-icon.sh` follows the layer. It does not ask whether the file is in the APK; it asks what the adaptive icon's `<background>` resolves to, through the resource table, in both build types, and then reads concept 04's six stops back out of what it found — which catches a stale APK as well as a lost overlay. Passing on a debug APK and on one put through `--shorten-resource-paths`, failing on a stale one, and saying NOT CHECKED rather than guessing when aapt2 is absent. What none of it can see is a pixel: banding on the OLED, the glow's weight under the Pixel's mask and how the falcon reads on a lit ground are a launcher screenshot on the phone, and ANDROID.md says so instead of implying the check covers it. One slot the ground reaches is not the launcher at all. `MediaHub.appIcon()` draws whatever `getApplicationIcon()` returns into a 512 px bitmap and hands it to the lock-screen card as the large icon for any track whose cover has not been decoded yet, so the card and the shade carry this ground as well — the quickest place for Kyle to look at it, and the only reader of the launcher icon inside the app. The walk reads that card's title and the session's playback state and never its icon, so no step changes meaning. The Android 12+ splash very likely changes as well, and the other way from what the docs' one-line version of the rule suggests: the platform drops an adaptive background only when it is a single colour close to `windowSplashScreenBackground`, which round 44's flat `#05080B` was, so the falcon has been drawing there alone and enlarged; a gradient is not one colour, so it is kept and the whole icon is drawn. Likely, not proven — that one is a screenshot too.
This commit is contained in:
1 parent
afcd3cc7f3
commit
da4abeb8c9
6 files changed
+314
-35
No files matched your search
+102
-19
@@ -30,7 +30,7 @@ implements is `research/mobile-mockup/` (gitignored; see its README).
|
||||
| `src-tauri/tauri.android.conf.json` | merged over `tauri.conf.json` on Android: one window at `mobile.html`, no desktop resources, `minSdkVersion` 26 |
|
||||
| `src-tauri/gen/android/` | the generated Gradle project (`npx tauri android init`); `minSdk = 26`. Gitignored |
|
||||
| `src-tauri/android/build.sh`, `deploy.sh` | the detour build (below) and build → install → launch → screenshot |
|
||||
| `src-tauri/android/res/` | the app module's own Android resources, tracked: the adaptive launcher icon, its monochrome layer, the legacy mipmaps and the one colour the background is. `build.sh` and `dev.sh` copy the whole tree into `gen/android/app/src/main/res/` before Gradle; `verify-icon.sh` reads it back out of the APK |
|
||||
| `src-tauri/android/res/` | the app module's own Android resources, tracked: the adaptive launcher icon, its monochrome layer, the legacy mipmaps and the gradient drawable the background is. `build.sh` and `dev.sh` copy the whole tree into `gen/android/app/src/main/res/` before Gradle; `verify-icon.sh` reads it back out of the APK |
|
||||
| `src-tauri/src/android_glue.rs` | `JNI_OnLoad`: hands the JavaVM and app Context to `ndk-context` (cpal's AAudio backend needs it; tao 0.35 never fills it) |
|
||||
| `src-tauri/src/updater_mobile.rs` | the updater's stand-in on mobile (`#[cfg(mobile)] #[path]`) |
|
||||
| `src-tauri/src/lyric_align_stub.rs`, `vocal_sep_stub.rs` | stand-ins off Windows for the two Windows ML modules |
|
||||
@@ -1613,7 +1613,10 @@ It carries:
|
||||
`ic_launcher_monochrome.png` (108–432 px) from the kit's
|
||||
`android/adaptive/res/`, and the legacy `ic_launcher.png` /
|
||||
`ic_launcher_round.png` (48–192 px) from its `android/legacy/`;
|
||||
- `values/ic_launcher_background.xml` — `#FF05080B`, the brand's night.
|
||||
- `drawable/ic_launcher_ground.xml` — the adaptive **background**: concept 04's
|
||||
gradient, the desktop icon's own ground, over the whole 108-unit layer;
|
||||
- `values/ic_launcher_background.xml` — `#FF05080B`, the brand's night. Not
|
||||
the background any more, and kept for its filename (below).
|
||||
|
||||
Three things about it are not obvious.
|
||||
|
||||
@@ -1626,13 +1629,45 @@ the generated project still carries Android Studio's template vectors at
|
||||
`drawable-v24/ic_launcher_foreground.xml`, so two of the three references
|
||||
resolve — to the green robot — and only `@drawable/ic_launcher_monochrome`
|
||||
fails the build. The overlay's XML says `@mipmap/` for the two image layers
|
||||
and `@color/` for the background instead.
|
||||
and a `@drawable/` of our own for the background instead.
|
||||
|
||||
**The background is one colour resource, not five identical flat PNGs.**
|
||||
A richer gradient is being mocked separately; when it is approved it replaces
|
||||
one file, and the three XML references do not move.
|
||||
**The background is a vector drawable, not a colour and not five PNGs.**
|
||||
`drawable/ic_launcher_ground.xml` carries the desktop icon's own ground —
|
||||
concept 04's navy surface and the cyan glow off its top-left — mapped onto the
|
||||
whole 108-unit layer, every number the kit's `src-tauri/icons/icon.svg`
|
||||
multiplied by 108/1024. The flat `#05080B` was three quarters of what a masked
|
||||
tile shows (the falcon covers about a quarter of it), and the glow survives
|
||||
every mask: the visible top-left corner goes from `#05080B` to `#0E3543`. The
|
||||
`rx="232"` on the kit's rects is not carried over; on the phone the launcher's
|
||||
mask does that job.
|
||||
|
||||
**The colour file is named `ic_launcher_background.xml` on purpose.**
|
||||
A vector rather than a PNG set because this artwork happens to fit one.
|
||||
Android's `android:type="radial"` is a circle — centre and radius, no
|
||||
transform of its own — and the kit's glow is
|
||||
`translate(270 220) rotate(43) scale(660)`, a **uniform** scale, so the
|
||||
ellipse *is* a circle and the rotation is a no-op. Had that scale been two
|
||||
numbers, no vector could have said it and this would be five rendered PNGs.
|
||||
The other cheap route, a `<layer-list>` of two `<shape>`s, is out on the
|
||||
linear axis alone: the concept's is 51.1° off horizontal, and a shape
|
||||
drawable's `android:angle` "must be a multiple of 45", with three evenly
|
||||
spaced stops at most. Folding `stop-opacity` into 8-bit alpha (0.18 → `2E`,
|
||||
0.05 → `0D`) costs at most one level of 255 against the SVG's float composite.
|
||||
And a vector reaches sizes no PNG here does: the media card draws this same
|
||||
drawable at 512 px (below) and the Android 12+ splash slot is 240 dp, about
|
||||
630 px on the Pixel, both past the 432 px an xxxhdpi PNG tops out at.
|
||||
|
||||
What a vector cannot do is **dither**. Both forms are 8-bit and quantise
|
||||
identically — 122 colour steps down the diagonal at 432 px, longest flat run
|
||||
43 px, over a total range of R 12 / G 49 / B 55 levels — so the form does not
|
||||
cause banding, but only a PNG could have been dithered out of it. If the
|
||||
phone's OLED contours, the answer is a dithered five-density set behind this
|
||||
same `@drawable/ic_launcher_ground` name: a file per density, and no XML
|
||||
moves.
|
||||
|
||||
**The colour file stays, and its filename is the whole reason.** Nothing
|
||||
points at `values/ic_launcher_background.xml` since the ground became a
|
||||
drawable — `values-v31/themes.xml` writes the splash colour as a literal on
|
||||
purpose — and deleting it would build. It is kept because
|
||||
`src-tauri/icons/android/values/ic_launcher_background.xml` declares the same
|
||||
resource name (`#fff`), and two files in one `values/` directory declaring
|
||||
one colour is a hard aapt2 error — `resource 'color/ic_launcher_background'
|
||||
@@ -1648,10 +1683,10 @@ it, and it should be brought to the kit before anyone builds through the CLI.
|
||||
### Checking it
|
||||
|
||||
`verify-icon.sh` opens the APK `build.sh` has just made and reads the
|
||||
resources back out. It checks the overlay's own two XMLs first, before the APK
|
||||
is opened — a `<monochrome>` layer that a later edit drops is a failure too —
|
||||
and then reads the APK, debug and release differently, because AGP hands them
|
||||
to aapt2 differently:
|
||||
resources back out. It checks the overlay itself first, before the APK is
|
||||
opened — a `<monochrome>` layer that a later edit drops is a failure, and so is
|
||||
a ground flattened back to one colour — and then reads the APK, debug and
|
||||
release differently, because AGP hands them to aapt2 differently:
|
||||
|
||||
- **debug** — the packaging default for a debuggable build type is
|
||||
`--no-crunch`, so every entry *is* the file on disk. Both
|
||||
@@ -1668,20 +1703,68 @@ to aapt2 differently:
|
||||
does not turn on), it says the icon is unchecked rather than claiming a
|
||||
failure it cannot see.
|
||||
|
||||
And in **both**, the ground. This one is not a name or a byte but a
|
||||
resolution: `<background>` is a reference, so the check asks `aapt2 dump
|
||||
resources` what `mipmap/ic_launcher` is at `anydpi`, reads that file's
|
||||
`<background>` id, maps the id back through the table, and requires
|
||||
`drawable/ic_launcher_ground`; then it follows the two `fillColor` references
|
||||
the `aapt:attr` blocks became and requires concept 04's six stops in them.
|
||||
That is the check that catches a **stale APK** as well as a lost overlay —
|
||||
`build.sh` pipes Gradle through `| tail -20 || true` and then runs
|
||||
`ls -la "$APK"`, so a Gradle run that failed leaves the previous build's file
|
||||
exactly where the next step looks for it, with every PNG in it still the
|
||||
falcon's. Without aapt2 on the machine this half says `NOT CHECKED` and the
|
||||
rest of the script still runs.
|
||||
|
||||
Both `build.sh` and `dev.sh` call it, so no APK this repo makes can quietly go
|
||||
back to the old artwork. It costs about a second and a half on the 107 MB
|
||||
debug APK.
|
||||
back to the old artwork. It cost about a second and a half on the 107 MB debug
|
||||
APK before the ground was added; the ground is one `dump resources` and three
|
||||
`dump xmltree` calls on top of that, measured at 0.3 s against that same real
|
||||
APK — aapt2 reads `resources.arsc` and the entries it names, never the whole
|
||||
file, so the cost does not grow with the APK. The release branch reuses the
|
||||
same table rather than asking aapt2 for it twice.
|
||||
|
||||
What it cannot check is what the icon looks like. `deploy.sh`'s `px-1.png` is
|
||||
a screenshot of the app five seconds after launch; the launcher is not in it.
|
||||
The one thing that may now differ there is the Android 12+ system splash,
|
||||
which is drawn from the launcher icon — if a cold start ever runs past those
|
||||
five seconds, that splash is the falcon on `#05080B` rather than tauri's mark.
|
||||
**Adaptive masking and the monochrome layer can only be judged from the
|
||||
phone's home screen**, which is a minute with the Pixel: the icon under the
|
||||
launcher's mask, in the app drawer, in recents and in another app's share
|
||||
sheet — and then Wallpaper & style → Themed icons on, to see the monochrome
|
||||
layer take the wallpaper's colour.
|
||||
five seconds, that splash is the falcon rather than tauri's mark. Whether
|
||||
the ground is under it there is the platform's call, and this round very
|
||||
likely changes the answer. `SplashscreenContentDrawer.processAdaptiveIcon`
|
||||
drops an adaptive icon's background and draws the foreground alone —
|
||||
enlarged, since it is then filling the slot by itself — only when that
|
||||
background is **not complex** *and* reads as the same colour as
|
||||
`windowSplashScreenBackground`. A background with more than one colour in it
|
||||
is kept and the whole icon is drawn, contrast or no contrast. Round 44's flat
|
||||
`#FF05080B` is exactly the colour round 45's `values-v31/themes.xml` writes
|
||||
there, so it was dropped and the falcon drew large on the flat night; a
|
||||
gradient is not one colour, so the likely new answer is the whole icon —
|
||||
this ground, under a falcon no longer enlarged to fill the slot. Likely,
|
||||
not proven: it is a screenshot, and the docs' own one-line version of that
|
||||
rule would have predicted the other way.
|
||||
**Everything about how it looks can only be judged from the phone**, and the
|
||||
gradient ground adds to that list rather than shortening it.
|
||||
`verify-icon.sh` proves the adaptive icon's `<background>` resolves to a
|
||||
drawable carrying concept 04's six stops and its geometry; it cannot see a
|
||||
pixel. So: the home screen under the launcher's mask, the app
|
||||
drawer, recents, another app's share sheet — reading for **banding** on the
|
||||
OLED above all, since that is the one thing the vector form cannot dither its
|
||||
way out of, then the glow's weight under the Pixel's circle and how the falcon
|
||||
sits on a lit ground rather than a flat one — and then Wallpaper & style →
|
||||
Themed icons on, which swaps both layers for the monochrome one over a
|
||||
background the wallpaper picks: the one slot this ground never reaches, on
|
||||
the list so that a round about the background can be seen not to have moved
|
||||
it.
|
||||
|
||||
**One consequence of this round is reachable without the launcher**, and it
|
||||
is the quickest of them to check. `MediaHub.appIcon()` in the media-session
|
||||
plugin draws whatever `getApplicationIcon()` returns into a 512 px bitmap and
|
||||
hands it to the lock-screen card as its large icon, for every track whose
|
||||
cover has not been decoded yet — so the card and the shade carry this ground
|
||||
now as well. Play a track with no cover and look at the card. Nothing else in
|
||||
the app reads the launcher icon, and the walk never reads the card's icon —
|
||||
only its `android.title` and the session's playback state — so no step
|
||||
changes meaning either way.
|
||||
|
||||
## Verifying
|
||||
|
||||
|
||||
Reference in new issue
Block a user