Files
Mykhailo ShevchukandClaude Opus 5 baa079f5f1 Loader: show the loading animation while an app's .fap is read (#1101)
* Loader: show the loading animation while an app's .fap is read

Launching an app drew nothing while the loader read and relocated its ELF
off the SD card, so the screen sat on the previous view looking like the
press had been ignored. NFC is the worst case - at 327 KB it is 2.5x the
next largest main-menu app - and a cold or fragmented card stretches it to
seconds, which is why it seems intermittent.

The animation was never missing, only unused here. loader_applications.c
already raises it around a launch from the Apps browser, and
loader_do_deferred_launch() around a chained one; what had none was
loader_do_start_by_name(), which every other entry point funnels through -
main menu, archive browser, desktop favourites and hold-buttons, RPC and
autorun - while loader_start_external_app() blocked the loader thread in
flipper_application_preload() and _map_to_memory().

Scoped to the .fap branch rather than the whole function: that storage_file_exists
test is the first point that knows the launch will touch the SD card at all.
Internal apps resolve above it and start in microseconds, so covering them
would only flash the hourglass for a frame. It also brackets the API-mismatch
retry as a single span, which bracketing inside loader_start_external_app()
would have split into two.

Launches nest - loader_do_deferred_launch() calls loader_do_start_by_name(),
and re-enters itself once per queued app on failure, up to LOADER_QUEUE_MAX_SIZE
deep - so show/hide is refcounted. Without it the inner hide would tear the
backdrop down mid-chain, which is the gap the deferred site exists to cover.
Both callers now go through the same pair, which also drops the stop/start
the old code inflicted on the animation at every link of a retry chain.
The counter follows furi_hal_power_insomnia_enter/exit: guarded at both ends,
bare furi_check so it survives release builds, where an unbalanced hide would
otherwise wrap to 255 and strand a fullscreen view that swallows all input.

send_to_front is load-bearing and runs on every show, not once at attach:
launched apps and the Apps browser add their viewports above ours, and
gui_view_port_find_enabled() takes the last one added.

Error dialogs raised from inside the load still render on top - dialogs
allocate a fresh ViewHolder per message and gui_add_view_port() pushes to
front - so the API-mismatch prompt is not covered by the view we sent to
front first.

Two things left uncovered on purpose. The desktop's DesktopGlobalBeforeAppStarted
handshake runs above the internal/external split and is budgeted at 3 s
(desktop.c), so reaching it would mean showing the hourglass for internal apps
too. And the hide fires when the app thread starts, not when it paints; closing
that needs a GUI "viewport drawn" signal that does not exist, and the deferred
path has always had the same shape.

Closes #1100

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* CHANGELOG: loader loading animation

Closes #1100

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 21:04:20 +03:00
..
2024-07-15 20:02:45 +03:00
2024-07-15 20:02:45 +03:00
2026-01-11 09:57:30 +03:00
2024-07-15 20:02:45 +03:00