Repository navigation
feat(settings): add clean status bar, network conditions, and per-app language - #14
Merged
Merged
Conversation
…tus bar /api/network now reads and sets the emulator console's latency and bandwidth alongside the radio toggle, and rejects throttles for physical devices. The new /api/status-bar enters or leaves SystemUI demo mode at 9:41 with full battery and signal. Demo mode cannot be queried, so entering it records the boot ID and a reboot reads back as live. Wi-Fi-only devices without a telephony service can still go offline.
The Device tab switches to a clean status bar and simulates LTE, 3G, Edge, or offline networks. iOS uses Baguette's status-bar and network endpoints, and Android uses serve-emu, with the same latency and bandwidth for each preset. Physical Android devices offer only offline. On iOS the panel explains that throttling reaches apps launched afterwards and offers a relaunch. serve-emu rejection messages now reach the panel instead of a bare HTTP status.
mobile_set_app_language sets one app's language and region, then relaunches it, leaving the device language unchanged. iOS simulators write AppleLanguages and AppleLocale to the app's own preferences, like Settings > Apps > Language; Android 13 and later use cmd locale. The App tab gains a Language section with common languages, including right-to-left ones. Device setting changes now report fixed-message errors and round-trip timings.
lauridskern
added this pull request to stack #16
October 14, 2026 13:03
Android 16's demo mode ignores the mobile data type and signal level and draws an empty "3G" icon, so the clean status bar hides the mobile icon and shows Wi-Fi as fully connected instead of with a no-internet mark. Demo mode is re-entered first because mobile changes only apply in a fresh demo session.
The ring sat 8px outside the screen and drifted off the Android frame's corners. The screen still takes focus on click or drag, so typing reaches the device.
The emulator console accepts network speed and delay but no longer applies them, so LTE, 3G and Edge did nothing. Android now points the device's global proxy at a pacing proxy on the Mac: emulators reach it at 10.0.2.2 and phones through adb reverse, so throttling works on both without root or an app relaunch. Local dev servers such as Metro stay at full speed, a user's own proxy is shown as Custom and never replaced, and the setting is cleared on Normal, on shutdown, and on the next read if its plugin process exited. The unused serve-emu console throttle is removed.
The card always fills two lines with the simulated speed and where it applies, and holds the iOS relaunch button, so switching conditions no longer shifts the panel.
SystemUI ignored the first demo broadcasts while its settings observer had not seen demo mode allowed, yet the clean marker was still written.
…xies - Pace each direction through one ordered queue and finish delivering before a tunnel closes. - Clear a throttle left by an exited plugin when an Android backend first starts. - Serialize throttle changes per device so concurrent views share one proxy. - Report Normal over the user's own proxy as custom, and keep iOS controls on bad replies. - Count expected outcomes, and clear throttles alongside the other shutdown steps.
# Conflicts: # src/server/app-control/tools.ts # src/ui/device-settings.ts # tests/app-control.test.ts
…e by role - iOS and Android settings are separate classes; Android's plain settings share one endpoint table. - One mapping turns radios and the throttle into a network condition for reads and changes. - The pacing proxy and the device bookkeeping live in separate modules; local traffic skips pacing by having no pacer instead of separate branches. - Throttle changes share one queue, and the Device tab splits into network and location parts. - Drop networkConditions, which every device now reports in full.
# Conflicts: # src/server/app-control/tools.ts # src/ui/app-tools.ts
- Plain HTTP headers and stream ends share the paced queue, so requests take one round trip. - A reply that breaks off closes the app's connection instead of crashing the plugin. - Cancelled requests stop their download, so they no longer use the device's link. - Android 12 and older report that per-app languages need Android 13. - Expected recovery failures pass through, and every serve-emu rejection raises an error.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the "More device settings: clean status bar, language, slow network" card for iOS simulators and Android, in one PR.
Clean status bar (Device tab): iOS uses Baguette's existing
/status-barendpoint, which wrapssimctl status_bar. Android uses a new serve-emu/api/status-barthat enters SystemUI demo mode (9:41, full battery and signal, no notification icons). Demo mode can't be queried, so serve-emu records the boot ID when it enters it, and after a reboot the setting reads back as live.Network conditions (Device tab): Normal, LTE, 3G, Edge, and Offline on both platforms, with the same latency and bandwidth.
/networkendpoint, which injectsVirtualNetwork.dylib. Throttling reaches apps launched after it was turned on, so the panel's network card says so and has a relaunch button.settings put global http_proxy) at a pacing proxy the plugin runs on the Mac.src/server/network-throttle/proxy.tspaces traffic, andnetwork-throttle/android.tsmanages each device's proxy setting. Emulators reach it at10.0.2.2, and physical phones reach it throughadb reverse. Apps pick it up right away, with no root and no relaunch.network speed/delay) was tried first and dropped: the current emulator accepts it but doesn't apply it.10.0.2.2:8081, localhost, and LAN or.localhosts, pass through at full speed.Per-app language (App tab, and the new
mobile_set_app_languagetool): sets one app's language and region, then relaunches it. The device language is unchanged. iOS writesAppleLanguages/AppleLocaleinto the app's own preferences domain, which is what Settings > Apps > Language does. Android 13+ usescmd locale set-app-locales. The App tab has a Language section with common languages, including RTL ones (Arabic, Hebrew).Why Baguette isn't patched: the binary we ship already has the status-bar and network endpoints, and language needs only
simctlanddefaults. serve-emu is our in-repo fork, so it's edited directly, and modified files carry the fork notice.Smaller fixes along the way:
HTTP 400.svc dataexits with "Can't find service: phone".Structure:
src/server/device-settings.tshas one class per platform. Android's plain settings share an endpoint table, and a single mapping turns the radios and throttle into a network condition for both reads and changes. The Device tab is split into network and location components that share a status card and pill row.The final commit bumps the version to 0.1.156.
Stacked on #15 (
feat/device-app-tools), which adds the App tab. Merge #15 first; GitHub retargets this PR tomainwhen the base branch is deleted.Telemetry
mobile_app_languageandmobile_set_app_languagego through the existing app control wrapper asapp_control.language/app_control.set_language. The App tab recordsui.app_tools.language. Language tags are not sent.device_settings.read/device_settings.<setting>. Android throttle adb failures report underdevice_settings.network. Throttling a device that already uses its own proxy is an expected outcome and counts throughserver.error.outcome. Recovering a throttle left by an exited plugin reports adb failures underdevice_settings.network_recovery. Proxy addresses and traffic are not sent.ui.device_settings.changeround trips and countsui.device_settings.failure, with the same surface and device-change exclusions as the App tab.docs/telemetry.mdis updated.Testing
npm test: 653 pass, 1 skipped.tests/network-throttle.test.tscovers:adb reverse) setup;tests/serve-emu-device-conditions.test.tscovers the demo-mode boot marker, waiting for demo mode to be allowed, and devices without telephony.device-settings,app-control, and the React UI test cover the new settings, language tools, relaunch, and validation. Invalid language tags never reach a device shell.npm run buildsucceeds.check:mcp-budget: 46 tools, 53,917 / 64,000 bytes.tscreports only errors that already exist on the base branch, in files this PR doesn't touch.de-ATreads back, relaunches the app, and resets.The clean status bar works after a fix in this PR (see the last commit), and
cmd localesets and reads per-app languages.Throttling measured on device: a 100 KB HTTPS download from the internet, timed inside each device. iOS is a URLSession probe spawned in the simulator; Android is
ncthrough the proxy at10.0.2.2.After the review fixes: a 100 KB HTTPS download with
Connection: closethrough the paced tunnel arrived complete at LTE (0.42 s), 3G (1.67 s), and Edge (5.25 s). The clean status bar applied on an emulator where demo mode started disallowed.Local servers aren't slowed: at Edge, a 100 KB download from a server on the Mac took 0.06 s.
Chrome picks up the proxy live: with Edge set, loading example.com sent its tunnels through the proxy, with no relaunch. Normal left
http_proxyand the marker unset.Physical Android phones were covered only by unit tests; none was connected.
Offline uses the existing
svctoggle and works on a healthy image.