Everything posted by Vitaly
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.37-betav1.2.37-beta — DL5 Cluster Recon tab + Telegram one-tap export A targeted release for the DiLink 5 tester whose Yandex Maps cluster resize broke after v1.2.35 reverted force_resizable_activities. This release ships only the recon tooling needed to validate the next fix in production conditions before wiring any live cluster operation. Why a recon-only release The DL5 resize regression has a clear suspected fix — the AOSP per-app compat override am compat enable FORCE_RESIZE_APP <pkg> (change ID 174042936) — but we need empirical confirmation that the compat framework shell surface is actually exposed on production BYD ROMs before putting any new live code path in front of users. To do that without burning hours of in-car tests, the tester now has a dedicated diag tab that runs 30 non-destructive probes and ships the full report back via Telegram in a single tap. New "Cluster DL5" diagnostic tab Renamed: the existing "Cluster" tab is now "Cluster DL3" (purely cosmetic, the underlying panel panel_cluster_poc is unchanged). New tab "Cluster DL5" at the end of the tab strip (index 11), with a Material 3 card UI: header (ic_science icon + title + subtitle), platform pill, live counters, three action buttons (Run recon / Copy report / Send via Telegram), and a programmatic test list. No live cluster operation in this release — every test is read-only. 30 recon tests R01–R05 Platform identity: Build.MODEL, Build.PRODUCT, Build.FINGERPRINT, DisplayManager inventory (cluster auto-detection by name match on fission|xdja|cluster|virtual), ADB local uid probe, SELinux enforce mode, cluster apps installed scan (Yandex Maps, Google Maps, Waze, Sygic, TomTom). R06–R12 Shell verb surface: full --help capture of am, am start, cmd activity, cmd activity task, wm, cmd window, cmd display — so we know exactly which --display, --windowingMode, resize, set-ignore-orientation-request flags are honoured on this ROM. R13–R18 Compat framework — the critical block. am compat --help, am compat list first 60 entries, then targeted am compat get against FORCE_RESIZE_APP, 174042936 (numeric), NEVER_SANDBOX_DISPLAY_APIS, OVERRIDE_MIN_ASPECT_RATIO, all scoped to our own package. R19–R23 Settings + dumpsys snapshots: force_resizable_activities current value, settings list global filtered on resizable|freeform|multi|sandbox|ignore_orientation, dumpsys window/display/activity keyword filters. R24–R25 wm size/wm density on default display and on the detected cluster display. R26–R28 Per-app manifest probe via pm dump <pkg> | grep -E 'resizeable|maxAspectRatio|launchMode|targetSdk' for Yandex Maps, Google Maps, Waze (auto-SKIP when not installed). R29 Service list filter for auto_container|cluster|fission|xdja|window|activity_task. R30 getprop filter for resizable|freeform|multi|sandbox|cluster|dilink|fission|xdja. Total run: < 20 s typical (single-threaded executor, 6 s per-probe timeout). Telegram one-tap export New helper AppLogger.shareReportToTelegram(Context, String): Probes 5 Telegram package names in order (org.telegram.messenger, org.telegram.messenger.web, org.telegram.plus, nekox.messenger, org.thunderdog.challegram). On hit, fires a direct ACTION_SEND with setPackage(hit) + FileProvider URI on a byd_report_*.log containing the recon report and the in-memory AppLogger ringbuffer (so we get both the structured probe results and the raw lifecycle log). Three-level fallback: TG-found → direct intent; no-TG → generic share chooser; no-file → text-only chooser. The new "Send via Telegram" button in the Cluster DL5 panel wires straight to this helper — no manual file-picking, no submenu hunting. Next step (v1.2.38) Once the tester runs the recon and ships the report, we will: Confirm am compat list contains FORCE_RESIZE_APP (or 174042936) accessible from the app uid 2000 shell. Wire the per-app fix into ClusterService.startActivityViaShell (chain am compat enable 174042936 <pkg> ; am force-stop <pkg> ; am start --display N --windowingMode 5 ...). Remove the now-unneeded global force_resizable_activities toggle that v1.2.35 reverted, since the per-app override has no system-wide side effect. Touched files app/build.gradle — 294 → 295, 1.2.36-beta → 1.2.37-beta. New dilink5/Dl5ClusterReconRunner.java (~380 LoC, reuses DiLink5TestRunner.TestDef/TestResult/Status/Listener types for row rendering reuse). New res/layout/include_diag_cluster_dl5.xml (~140 LoC, Material 3 card + action row + programmatic test list). AppLogger.java — added shareReportToTelegram (~65 LoC). DiagActivity.java — new TAB_CLUSTER_DL5 = 11, new fields, new methods bindClusterDl5Panel / prepareClusterDl5TestRows / runClusterDl5AllTests / updateClusterDl5Counters / copyClusterDl5Report / sendClusterDl5ToTelegram (~110 LoC). Row binder reused from existing bindDl5Row. res/layout/activity_diag.xml — new tab + new include. res/values/strings.xml + res/values-en/strings.xml — 1 renamed (diag_tab_cluster Cluster → Cluster DL3) + 15 new keys. Compatibility No new permission, no manifest change, no new dependency, no protocol bump. Zero behavioural regression on DL2 / DL3 / DL4 / DL5 production paths — the new tab is purely diagnostic; the rename Cluster → Cluster DL3 is cosmetic and does not affect any code path. Channel Pre-release on beta/1.2.0-dilink5. Not merged to main. Latest stable remains v1.0.1 on GitHub. LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.36-betaDashCast v1.2.36-beta — TetherFi Hotspot integration (build 294) Pre-release. Not for daily-driver yet — only the hotspot subsystem changed; everything else is identical to v1.2.35-beta. Highlights Full Hotspot screen redesign built around the open-source TetherFi proxy (no carrier tether quota, no BYD MIUI tether restrictions). Live traffic stats (uptime / RX / TX) sampled every 5 s without root. Connected clients list populated from dumpsys wifip2p + /proc/net/arp via the on-device ADB shell proxy. One-shot TetherFi update checker that pings GitHub Releases at boot and surfaces an "Update available" badge on the hotspot card. What's new in detail Hotspot UI (activity_hotspot.xml) New landscape-first 2-column layout matching the project mockup. Left column — Hero card: large status pill (running / stopped / unknown), update-available badge, primary actions (Start / Stop / Toggle / Open TetherFi). Right column — three stacked cards: Clients & traffic — connected-client count pill, scrollable client rows (icon + friendly name + IPv4), live uptime / RX / TX counters. Watchdog — auto-restart switch (decoupled from stats sampling so stats keep working when the watchdog is OFF). Autostart on boot — toggle persisted in SharedPreferences and consumed by BootReceiver. New drawables: bg_hero_icon, bg_side_icon, bg_count_pill, ic_devices, ic_monitor_heart, ic_wifi_tethering, ic_sim_card. TetherFi integration (HotspotActivity.java) Single supported entry point: com.pyamsoft.tetherfi/.activity.ProxyTileActivity with key_action ∈ START / STOP / TOGGLE. No reflection, no private APIs. Independent stats probe (statsTick, 5 s period) probes dumpsys activity services com.pyamsoft.tetherfi | grep ProxyForegroundService via the local ADB client. Watchdog and stats now run on separate cadences. Session lifecycle (onProbeTransition): DOWN → UP : snapshot SystemClock.elapsedRealtime() + TetherFi UID + RX/TX baselines from TrafficStats.getUidRxBytes / getUidTxBytes. UP → DOWN : reset all counters to -1 and clear the clients list. TetherFi UID resolved once via PackageManager and cached for the activity lifetime. Format helpers: formatUptime(ms) → Hh Mm Ss, formatBytes(b) → B / KB / MB / GB, friendlyMacName(mac) → Device …XX:XX fallback. Stats handler properly torn down on onPause (no callbacks leak). Connected clients enumeration New combined ADB shell payload: dumpsys wifip2p 2>/dev/null; echo '===ARP==='; cat /proc/net/arp 2>/dev/null. Static parseClients(String) splits on the separator, builds a MAC → IPv4 map from arp, then walks the wifip2p dump looking for client sections (Client list, mClients, clients:, wifip2pdevice lines) and pairs each MAC with the nearest deviceName= token. Static inner type HClient { mac, name, ip } + regex P_MAC / P_NAME. renderClients(List<HClient>) updates tv_clients_count, repopulates ll_clients_list via buildClientRow(c) (programmatic LinearLayout with an ic_devices icon, name TextView weight=1, monospace IP TextView), and toggles tv_clients_empty. Per-client RX/TX is intentionally not shown — kernel does not expose per-MAC counters without root. TetherFi update checker (TetherFiUpdateChecker.java) Background HEAD/JSON probe against the TetherFi GitHub Releases API at boot. Compares installed APK version (PackageManager.getPackageInfo) against tag_name. Result cached in SharedPreferences; the hotspot card reads it on resume and shows the Update available badge with a link button. Pure best-effort: any network failure / parse failure is swallowed silently. Boot autostart (BootReceiver.java) New autostart path triggered by the existing BOOT_COMPLETED receiver: when the new pref hotspot_autostart_on_boot is true, fire ProxyTileActivity with key_action=START as part of the existing boot fan-out. Also kicks TetherFiUpdateChecker once. Strings (values/strings.xml + values-en/strings.xml) 40+ new strings for the redesigned hotspot screen (titles, empty states, stats labels, update-available copy). Both fr (default) and en localized. Misc MainActivity / SettingsActivity / activity_main.xml / activity_settings.xml: small wiring tweaks to surface the hotspot entry point with the new card style. Platform.java: minor helper added for the hotspot card. versionCode 294, versionName 1.2.36-beta. Known limitations Per-client bandwidth: not supported (root required to read iptables per-MAC counters). Client list only shows devices currently in the ARP table — peers without recent IP traffic may briefly be missing. Update checker requires internet on the head unit at boot; otherwise the badge appears on the next app launch with connectivity. Install Sideload DashCast-v1.2.36-beta-debug.apk. Platform-signed; no uninstall needed when upgrading from v1.2.35-beta. LinksAPK download GitHub release page
-
BYD’s Denza Z9 GT Chopard edition sold for 815,000 USD, most expensive Denza ever
A one-off Denza Z9 GT, created jointly by BYD and Swiss luxury watch and jewellery brand Chopard, sold for 700,000 euros, approximately 5.53 million yuan (815,000 USD), at the 32nd Cannes amfAR charity gala in France, announced by BYD. The proceeds from the auction will support AIDS research programs. Denza Z9 GT Chopard edition The customised shooting brake was presented during the Cannes Film Festival activities in Antibes, France, and was described by BYD as a globally unique build created specifically for the charity auction. The vehicle received exclusive gold exterior accents and a redesigned interior inspired by Chopard jewellery craftsmanship. The cabin includes amethyst-themed trim pieces, gemstone-style physical controls, customised embroidery featuring Chopard’s “C” logo, branded wireless charging surfaces, and a dedicated infotainment interface theme. The collaboration also included matching “His & Hers” luxury watches and a custom luggage collection. BYD executive vice president Li Ke said the project combined automotive technology and luxury craftsmanship while supporting fundraising efforts at the amfAR gala. Chopard co-president and artistic director Caroline Scheufele described the vehicle as a jewellery-inspired interpretation of the Z9GT platform. Denza Z9 GT technology The standard Denza Z9 GT officially entered customer deliveries in March. In China, the model is priced between 269,800 yuan and 369,800 yuan (39,000 USD to 53,500 USD). The Z9 GT is among the first production vehicles equipped with BYD’s second-generation Blade Battery and flash charging technology. Previous demonstrations also showed the model participating in BYD’s DiSus intelligent body control system showcases during recent SUV technology events involving multiple Chinese automakers. Earlier this month, a Denza Z9 GT owner in China disclosed that replacing the vehicle’s Blade Battery 2.0 pack would cost less than 11,600 USD, according to a dealership quotation. In April, BYD also announced plans to deploy 6,000 overseas flash chargers while expanding Denza operations in Europe with the Z9GT and D9 models. Denza Z9 GT sales in China. Credit: China EV DataTracker High-value BYD vehicle sales The Cannes charity auction is among several recent high-value BYD vehicle transactions disclosed publicly in 2026. In April, BYD confirmed that the Yangwang U9 Xtreme electric supercar sold for more than 20 million yuan (2.76 million USD) during the 2026 Beijing Auto Show, becoming the highest-priced BYD vehicle transaction publicly disclosed to date. The Denza Z9GT Chopard edition, therefore, ranks below the Yangwang U9 Xtreme transaction in publicly disclosed BYD vehicle sales value, while remaining one of the company’s highest-profile international charity auction appearances. Denza Z9GT sales According to China EV DataTracker, the Denza Z9 GT recorded 3,969 domestic sales in April 2026, up from 712 units in March and 154 units in February. The model accounted for 37.3% of Denza brand sales in April. The Z9 GT previously recorded monthly sales of 744 units in May 2025 and reached 855 units in August 2025 before declining later in the year. Customised Denza Z9GT features Chopard-inspired gemstone interior accents and gold trim. Updated: 25/05/2026 3:33 China time
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.35-betav1.2.35-beta — Revert force_resizable_activities side effect on DiLink 5 Hotfix release on top of v1.2.34-beta. Three commits, all focused on undoing a global Android setting that v1.2.32 – v1.2.34 had unintentionally turned on system-wide on DiLink 5 head units. Internal versionName: 1.2.35 · versionCode: 283. The bug Since v1.2.32 (build 223) DashCast had been running, on every DL5 cluster launch: settings put global force_resizable_activities 1 The goal was legitimate: the cluster's freeform root task on display=3 is only honored by activities whose manifest declares android:resizeableActivity="true", and most BYD navigation apps (Yandex Maps, Yandex Navi, Google Maps, …) declare it false, so ATM was coercing their task back to fullscreen. Setting the force_resizable_activities developer-option global makes ATM ignore the manifest flag and treat every activity as resizable, which is exactly what the cluster freeform path needed. But that global is system-wide and persistent (Settings.Global). DashCast was setting it on every launch and never resetting it, so every other app on the head unit also became resizable. Field-reproduced side effect: BYD's 360° camera app — normally locked to fullscreen — started accepting split-screen and freeform on DL5 head units that had ever opened DashCast since v1.2.32. The setting also survives DashCast uninstall. The fix v1.2.35 stops setting the flag entirely and proactively heals devices that already received v1.2.32 – v1.2.34. DiLink 2 / 3 / 4 are not touched (they never set the flag in the first place). Changes since v1.2.34-beta 1. ClusterService.startActivityViaShell() — stop setting the flag. The settings put global force_resizable_activities 1 prefix has been removed from the DL5 launch command. The cluster freeform root task is still created via am start --display 3 --windowingMode 5, and per-task cmd activity task resize still applies to apps whose manifest already allows resize. Apps that declare resizeableActivity="false" will no longer be forced into freeform on the cluster, which matches their author's intent and the OS contract. 2. ClusterService.onCreate() — DL5-only one-shot cleanup. On every DashCast launch on DL5, the service now runs: v=$(settings get global force_resizable_activities) if [ "$v" = "1" ]; then settings put global force_resizable_activities 0 echo RESET else echo OK=$v fi Gated by AdbLocalClient.isDiLink5Safe(context), so DL2 / 3 / 4 send no shell at all. The check is idempotent and cheap (one shell roundtrip per onCreate), and ClusterService is started from MainActivity.onCreate() via startForegroundService(), so simply opening DashCast once on an affected car restores the device. Note that activities already running keep their resizable state until they are killed and relaunched (Android does not re-evaluate the global on live processes), but new launches honor the new value immediately — no reboot required. 3. SysInfo — new "Force Resizable" status row (DL5 only). The Services card in the SysInfo screen now shows a new row, rendered only on DL5: Label: Force Resizable Sub-text: DL5 dev override · value 0 (or 1, reflecting the current Settings.Global value) Badge: RUN (green) when the flag is 1, OFF (red) when it is 0 Tap on the leading icon: toggles the value (1↔0), then re-reads the setting and refreshes the row so the user can visually confirm the change. The implementation reuses the same addServiceRow(..., alwaysClickable=true) pattern as the Projection state row introduced in v1.2.79. Why no reboot is needed force_resizable_activities is a Settings.Global row read live by ActivityTaskManager at activity / task creation. Setting it back to 0 takes effect immediately for any subsequent activity launch. Apps already in memory keep their current resizable state until killed (e.g. swiped from recents or am force-stop), which is a one-time cost on the affected devices and does not require rebooting the head unit. Files touched app/build.gradle — versionCode 282 → 283, versionName 1.2.34 → 1.2.35 app/src/main/java/com/byd/dashcast/ClusterService.java — remove force-set, add DL5 cleanup app/src/main/java/com/byd/dashcast/SysInfoActivity.java — new row + probeForceResizable() + toggleForceResizable() app/src/main/res/values{,-ar,-be,-de,-en,-es,-it,-kk,-ru,-tr,-uk,-uz}/strings.xml — 3 new keys × 12 locales Install APK: DashCast-v1.2.35-beta.apk (15 MB, debug, BYD platform-signed). versionCode 283 — every car running an older DashCast will detect this as an update. Compatibility Tested on BYD Seal EU (DiLink 3, fission VirtualDisplay id 1, 1920×720). DiLink 2 / 3 / 4: behaviour unchanged. DiLink 5: the side effect is healed on first launch of DashCast. Cars where the 360° camera (or any other normally non-resizable BYD app) was wrongly accepting split-screen will recover after killing the affected app from recents (or rebooting). LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.34-betav1.2.34-beta — Cluster projection lifecycle hardening + non-live resize editor Builds on top of v1.2.33-beta with 13 commits focused on the cluster projection lifecycle (start/stop reliability, automatic re-arming after a stop, instant first activation on app launch) and a major rework of the in-car resize editor (no more flashes, fullscreen-safe gesture handling). Internal versionName: 1.2.85 · versionCode: 281. Highlights Projection lifecycle is now self-healing. Stopping the projection and tapping any app afterwards re-activates the cluster automatically; previously the second activation silently failed with a launchDisplayId=-2 SecurityException. Faster first activation. The fast/warm/slow path now correctly recognises that Qt is still in projection mode after a stop, so the very first app tap after a stop is processed in the warm path (no full 6 s replay). Cluster resize editor — flash-free by default. The frame editor used to issue a moveAndResize on every drag, causing visible cluster flashes; the new editor only applies on Valider, with an opt-in Live preview switch (with a warning popup) for users who prefer the old behaviour. Fullscreen resize is now usable. Edge swipes (back, home, recents, notifications) no longer steal touches when the frame is at the screen border — the editor reserves the full per-edge area (within Android's 200 dp limit). Simpler UI. The Capture / Activate buttons are gone (auto-activation on first app tap, always-on); the SysInfo panel gains a Projection state row + a permanent slow-path replay action. Changes since v1.2.33-beta v1.2.85 — Cluster control card fullscreen-only + edge gesture exclusion Bug fix (apps page): the cluster control card (Ajuster / ↺ / Split / ⌨) no longer leaks into the apps page preview. It is now revealed exclusively when the mirror is expanded to fullscreen and hidden again on exit. Internal: showMirrorView() no longer sets panel_cluster_control visible; enterFullscreenMirror() does. The four sites that used the panel visibility as a proxy for "is the mirror active?" now check frameMirror (the actual TextureView host). setDashboardOffState() also hides the panel defensively. Bug fix (resize editor): system swipe gestures no longer steal touches when the resize frame is at a screen edge. ResizeFrameView.updateGestureExclusion() now reserves 198 dp strips along every canvas edge (capped just below the 200 dp Android limit) in addition to the existing 48 dp handle pads. v1.2.84 — Non-live resize editor by default + Live preview switch The resize editor no longer triggers Phase4Verbs.moveAndResize on every drag/preset (which caused noticeable cluster flashes when scrubbing the frame). Drag/preset updates now only paint the overlay; the cluster is updated only when the user taps Valider. Valider applies and stays open (was: apply + finish). New floating Close button (top-end IconButton) to dismiss the editor. New MaterialSwitch (top-start) to opt-in to live preview; toggling it ON shows a MaterialAlertDialog warning about cluster flashes. State is persisted in SharedPreferences (cluster_resize_live_mode). 5 new i18n strings × 12 locales (Italian apostrophe escaped). v1.2.83 — Auto-restart projection on app tap after stop After a Stop Projection, mClusterService is not null (still bound) but getDisplayId() == -1. The previous auto-activation logic in onSendToDashboard() only handled the service null case, so tapping an app after a stop hit a launchDisplayId=-2 SecurityException. Fix: also queue the pending app + call activateCluster() when the service is bound but getDisplayId() <= 0. v1.2.82 — Sync ClusterService display state after SysInfo replay stopProjectionNoAdb() sets mLauncher.setDashboardDisplayId(-1) + stopSelf(). The service stays alive (MainActivity bound) but the display state is wiped. After a SysInfo "replay" the bus-level sendInfo verbs were re-triggered but ClusterService was never told to re-discover the display, leading to launchDisplayId=-2 SecurityException on the next launch. Fix: call ClusterService.getInstance().restartProjection() (or startForegroundService(ClusterService.class) if null) after notifyProjectionActive() in replayProjectionSlowPath(). MainActivity.onStart() also re-attaches the listener and, if mClusterService.getDisplayId() > 0, calls updateDashboardStatus(mCurrentDashboardApp) + setActivateBtnEnabled(true) to sync the UI. v1.2.80 — Reduce sendInfo inter-command delay 6 s → 3 s The 6 s spacing between consecutive sendInfo calls in the slow path was conservative; field testing on DiLink 3 (Seal EU) shows 3 s is sufficient and halves the cold-start latency. v1.2.79 — SysInfo: Projection state row + always-on slow-path replay button The SysInfo panel now surfaces the current projection state (Qt mode + virtual-display lifecycle) at the top. The slow-path replay action is now always available (was: only when projection was OFF), so the user can force a clean re-arming whenever the cluster gets into an inconsistent state. v1.2.78 — Fix fast-path: track Qt projection mode independently of VD lifecycle The fast-path check was gated on the virtual display being alive, but on a stop the VD is destroyed while Qt itself remains in projection mode for a few seconds. New flag sQtInProjectionMode is set/cleared by notifyProjectionActive/Stopped and consulted by the fast/warm/slow split, so a tap immediately after a stop now correctly takes the warm path instead of replaying the whole sequence. v1.2.77 — Auto-start projection on app launch is now always-on The "auto-activate on app tap" setting is no longer a user preference; it is the only flow. Simplifies the codebase and matches user expectation. v1.2.76 — Simplify UI: drop Capture/Activate buttons + auto-activate on app tap + SysInfo restart play-button Removed the Capture and Activate buttons from MainActivity (their functions are now automatic: capture happens implicitly, activation triggers on the first app tap). Added a small play action in the SysInfo panel header for manual restart of the slow path. v1.2.75 — Auto-démarrage de la projection à l'ouverture (build 271) The projection is now armed automatically when the app opens (previously the user had to tap Activate first). v1.2.74 — Stop projection: restauration cluster d'origine (build 270) Stop projection now also restores the original cluster surface (sendInfo(0) to resume the native video stream) in addition to sendInfo(18) to close the projection tunnel. v1.2.72 — Cluster resize ergonomie voiture (build 268) Larger touch targets and snap-to-edges in the resize editor. v1.2.71 — Cluster Resize Activity (option B minimal) (build 267) New dedicated full-screen Activity for cluster resize (replaces the previous in-MainActivity inline editor). v1.2.70 — Cluster fission Move/Resize POC + verb library (build 266) First end-to-end implementation of Phase4Verbs.moveAndResize using setLaunchBounds + setLaunchWindowingMode(FREEFORM), gated to DL3 (Seal EU) for now. Build / install APK: DashCast-v1.2.85-debug.apk (15 MB, debug, signed with the BYD platform key). versionCode is 281, higher than every previously published pre-release (v1.2.33-beta shipped versionCode 229), so any car already running an older DashCast will detect this as an update. Min SDK 28 · Target SDK 29 · Compile SDK 33. Compatibility notes Tested on BYD Seal EU (DiLink 3, fission VirtualDisplay id 1, 1920×720). The non-live resize editor change is platform-agnostic (does not touch Phase4Verbs.moveAndResize itself — the v1.2.70 cascade is frozen). 12 locales (default fr + ar/be/de/en/es/it/kk/ru/tr/uk/uz) carry the new strings (resize_btn_close, resize_live_label, resize_live_warn_title, resize_live_warn_msg, resize_live_warn_ok). LinksAPK download GitHub release page
-
BYD Sealion 06 DM-i arrives at dealerships ahead of May 26 launch with up to 310 km EV range
BYD’s updated 2026 Sealion 06 DM-i has begun arriving at Chinese dealerships ahead of its official launch on May 26, with the company adding a longer-range plug-in hybrid variant featuring up to 310 km CLTC electric range and optional lidar-assisted driving functions. Dealer information circulating in China indicates the new variants may include the 305 Long-Range and 305 Flagship trims, with pre-deduction dealer pricing of 150,000 yuan (22,100 USD) and 160,000 yuan (23,600 USD). An optional lidar package bundled with BYD’s DiPilot 300 assisted-driving system is priced at 12,000 yuan (1,770 USD). Longer-range Sealion 06 DM-i The updated Sealion 06 DM-i continues to use BYD’s fifth-generation DM hybrid system. Earlier regulatory filings from February 2026 described a development-stage configuration featuring a 175 kW electric motor and an estimated 220 km CLTC pure-electric range target, along with an optional lidar-assisted driving setup under BYD’s “God’s Eye” system. More recent dealership-level information published ahead of the launch indicates that the production version has been equipped with a 38 kWh battery pack and a CLTC pure-electric range of 305 km for certain variants. BYD’s official launch preview further lists a maximum CLTC electric range of 310 km and a combined range of 1,845 km, alongside fuel consumption of 3.3 L/100 km under depleted battery conditions. The 1.5-litre engine remains rated at 74 kW, while the drive motor output increases to 175 kW. The charging power has been upgraded to 74 kW. The suspension setup retains front MacPherson and rear multi-link architecture, together with the DiSus-C intelligent damping body control system featuring road preview capability. BYD is expected to confirm final specification details during the official launch event on 26 May. DiPilot 300 lidar option Exterior styling of the updated Sealion 06 DM-i remains largely unchanged from the current model. A lidar-equipped configuration is offered as part of the higher-spec DiPilot 300 package, integrating BYD’s “God’s Eye” assisted driving system. The setup supports expanded navigation-assisted driving functions across urban and highway scenarios, as well as parking assistance. Standard variants continue to use the DiPilot 100 system, also referred to as “God’s Eye C” in BYD’s naming structure. BYD previously revealed the refreshed Sealion 06 EV in March with up to 710 km range and flash charging capability. Cabin upgrades The interior of the updated Sealion 06 DM-i is expected to follow the layout and feature set of the Sealion 06 EV, while adopting minor trim-specific adjustments for the plug-in hybrid version. Features expected to carry over from the Sealion 06 EV include a 15.6-inch floating centre screen, BYD’s DiLink smart cockpit system, a W-HUD head-up display, a 50W wireless charging pad, an electronic column-mounted shifter, and a rear flat-floor seating layout. Comfort-oriented equipment also includes a panoramic glass roof with a powered sunshade, a 12-speaker Dynaudio audio system, and a fragrance system. Dealership-reported features specific to the DM-i variant include a zero-gravity front passenger seat with powered leg support, as well as front seats offering ventilation, heating, massage, memory, and welcome functions. BYD Sealion 06 sales in China. Credit: China EV DataTracker Sales context According to China EV DataTracker data, Sealion 06 domestic sales reached 19,649 units in April 2026, up 7.7% from March. Monthly deliveries stood at 18,237 units in March, 6,287 in February, and 6,640 in January. New Sealion 06 DM-i interior adds zero-gravity passenger seat and massage functions.
-
BYD’s Denza Z9 GT Chopard edition sold for 811,000 USD, most expensive Denza ever
A one-off Denza Z9 GT, created jointly by BYD and Swiss luxury watch and jewellery brand Chopard, sold for 700,000 euros, approximately 5.53 million yuan (811,000 USD), at the 32nd Cannes amfAR charity gala in France, announced by BYD. The proceeds from the auction will support AIDS research programs. Denza Z9 GT Chopard edition The customised shooting brake was presented during the Cannes Film Festival activities in Antibes, France, and was described by BYD as a globally unique build created specifically for the charity auction. The vehicle received exclusive gold exterior accents and a redesigned interior inspired by Chopard jewellery craftsmanship. The cabin includes amethyst-themed trim pieces, gemstone-style physical controls, customised embroidery featuring Chopard’s “C” logo, branded wireless charging surfaces, and a dedicated infotainment interface theme. The collaboration also included matching “His & Hers” luxury watches and a custom luggage collection. BYD executive vice president Li Ke said the project combined automotive technology and luxury craftsmanship while supporting fundraising efforts at the amfAR gala. Chopard co-president and artistic director Caroline Scheufele described the vehicle as a jewellery-inspired interpretation of the Z9GT platform. Denza Z9 GT technology The standard Denza Z9 GT officially entered customer deliveries in March. In China, the model is priced between 269,800 yuan and 369,800 yuan (39,000 USD to 53,500 USD). The Z9 GT is among the first production vehicles equipped with BYD’s second-generation Blade Battery and flash charging technology. Previous demonstrations also showed the model participating in BYD’s DiSus intelligent body control system showcases during recent SUV technology events involving multiple Chinese automakers. Earlier this month, a Denza Z9 GT owner in China disclosed that replacing the vehicle’s Blade Battery 2.0 pack would cost less than 11,600 USD, according to a dealership quotation. In April, BYD also announced plans to deploy 6,000 overseas flash chargers while expanding Denza operations in Europe with the Z9GT and D9 models. Denza Z9 GT sales in China. Credit: China EV DataTracker High-value BYD vehicle sales The Cannes charity auction is among several recent high-value BYD vehicle transactions disclosed publicly in 2026. In April, BYD confirmed that the Yangwang U9 Xtreme electric supercar sold for more than 20 million yuan (2.76 million USD) during the 2026 Beijing Auto Show, becoming the highest-priced BYD vehicle transaction publicly disclosed to date. The Denza Z9GT Chopard edition, therefore, ranks below the Yangwang U9 Xtreme transaction in publicly disclosed BYD vehicle sales value, while remaining one of the company’s highest-profile international charity auction appearances. Denza Z9GT sales According to China EV DataTracker, the Denza Z9 GT recorded 3,969 domestic sales in April 2026, up from 712 units in March and 154 units in February. The model accounted for 37.3% of Denza brand sales in April. The Z9 GT previously recorded monthly sales of 744 units in May 2025 and reached 855 units in August 2025 before declining later in the year. Customised Denza Z9GT features Chopard-inspired gemstone interior accents and gold trim.
-
BYD: assisted driving slashes severe accident rate to one-sixth across 3 million vehicles
BYD said its assisted driving systems are now deployed across more than 60 vehicle models, and nearly 3 million vehicles, with severe accident rates reduced to one-sixth of human driving levels based on airbag-triggered incidents per 10 million kilometres, according to Autohome. The figures were presented by Yang Dongsheng, BYD Group senior vice president and head of the Automotive New Technology Research Institute, during the 13th Intelligent Connected Vehicle Technology Annual Conference held in Shanghai on May 21. Large-scale deployment Yang said BYD began expanding its deployment of intelligent driving in early 2025 and now offers L2-assisted driving systems across almost its entire passenger-car lineup. According to the presentation, navigation-assisted driving activation rates exceeded 50%, while parking-assistance usage reached 86%. BYD also stated that parking-related scratches and minor collisions declined to approximately one-fiftieth of human-driving levels under its parking assistance system. The company referenced its “God’s Eye” intelligent driving system and the associated parking-assistance guarantee program introduced in July 2025. The speech comes ahead of BYD’s May 28 intelligent-driving strategy event, which has triggered industry speculation over a broader rollout of updated God’s Eye systems and additional vehicle integration plans. Yang Dongsheng, BYD Group senior VP, at the 13th Intelligent Connected Vehicle Conference in Shanghai. Xuanji architecture and AI models Yang attributed part of the system integration capability to BYD’s Xuanji Architecture, which combines the electronic architecture and electrification systems into a unified vehicle platform. According to the presentation, BYD uses cloud-based world models and reinforcement learning for long-tail simulation training, supported by 190 million km of daily driving data generation. Yang said the company currently performs algorithm iterations every three days. BYD also described the use of physical AI models on the vehicle side for predictive driving functions and defensive-response calculations. The company said the system combines visual occupancy network detection with lidar-based occupancy detection to identify suspended and hollow obstacles during parking scenarios. BYD Global EV Sales BYD global EV sales till April 2026. Credit: China EV DataTracker Extreme-condition testing The presentation also focused on extreme-condition development, including high-speed tyre blowouts, rain, snow, and low-adhesion road surfaces. Yang said BYD’s integrated intelligent-electric platform can stabilise a vehicle within 200 milliseconds through coordinated motor and chassis responses. He stated that tire-blowout stability testing exceeded 200 km/h under company validation scenarios. BYD added that it operates dedicated proving grounds for parking and driving-assistance development to combine simulation and physical testing. Recent BYD product launches have increasingly integrated intelligent driving and fast-charging technologies. The updated Atto 3, introduced in China this week, adds 120 km of CLTC range alongside flash charging, while the upcoming Seal 08 sedan is scheduled to launch with rear-wheel steering and flash charging during the second quarter. BYD sold 314,100 electrified vehicles globally in April 2026, up 6.2% month-on-month but down 15.7% year-on-year. March deliveries totalled 295,639 vehicles, according to Chian EV DataTracker data.
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.33-betaPre-release beta — DiLink5 hardening cycle LOT 5 (clôture). LOT 5 — Toast lifecycle (build 229) #28 Toast.makeText() → applicationContext — 20 sites dans MainActivity passent de MainActivity.this/this/activity à getApplicationContext(). Élimine les warnings NotificationService: Toast already killed observés dans le sniffer 20260523_204155 à 20:41:58 et 20:44:03 quand l'Activity finit entre makeText() et show(). Comportement utilisateur strictement identique. Vérifié sans action #17 signature permission broadcast IBinder — déjà entièrement implémenté depuis LOT 2 / 1.2.30. MirrorDaemon.broadcastBinder() utilise sendBroadcast(intent, PERM_DAEMON_READY), manifest déclare <permission protectionLevel="signature"/>. ⚠️ Ajouter broadcastPermission côté registerReceiver casserait le binder (le daemon tourne en uid=2000 shell, ne détient pas les perms signature de notre package). Option A est au max safe. Différé volontairement #29 i18n audit — gap de 3-6 strings × 10 locales. Différé : qualité de traduction LLM variable, bénéfice marginal. Fin du cycle DiLink5 hardening LOT Commit Build Items LOT 1 1aff110 225 5 P1 fixes LOT 2 fe153bf 226 10 P2 fixes LOT 3 54ee184 227 6 P2 perf LOT 4 8196654 228 3 hygiene + Waze retry LOT 5 6c7351b 229 Toast lifecycle Total : 25 fixes vérifiés réels, 5 faux positifs rejetés, 0 régression remontée terrain. Branche beta/1.2.0-dilink5 prête pour merge vers main après validation device de cette build. LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.32-betaPre-release beta — DiLink5 hardening cycle LOT 4. LOT 4 — hygiene + 1 bonus fix (build 228) #24 Repo hygiene — 7 patch_*.py scripts déplacés de MyBYDApp/ racine vers MyBYDApp/scripts_archive/ (filesystem only, ils étaient déjà gitignored). #25 ClusterService.sIsRunning → volatile — écrit main thread, lu workers (auto-resize-thread, BetaProxyClient). Évite stale read. #26 Dead code purge — AdbLocalClient.captureClusterDisplay + BitmapCallback interface supprimés (0 caller). Imports Bitmap/BitmapFactory pruned. Bonus (diagnostiqué depuis le sniffer 20260523_204155) #27 Waze taskId race fix — MainActivity.autoApplyInsetsIfNeeded() retry findRunningTaskId jusqu'à 3× avec 500 ms backoff quand taskId<=0. Le log a montré Waze lancé à 20:42:25.669 mais absent de dumpsys recents à 20:42:26.696 (1027 ms après) → resizeActiveTask abortait. Fenêtre 1.5 s couvre le lag dumpsys sur DL3. Early-exit si l'utilisateur change d'app entre-temps. Faux positifs vérifiés (skip) btnCapture n'est PAS dead (placeholder intentionnel + layout + 11 locales) ImageView import encore utilisé Cache resolveClusterDisplayId → risque d'invalidation (cluster dynamic) Cache réflexion → aucune dans hot path Toast lifecycle → différé LOT 5 Cycle de hardening LOT 1 (1aff110) — 5 P1 fixes LOT 2 (fe153bf) — 10 P2 fixes LOT 3 (54ee184) — 6 P2 perf fixes LOT 4 (8196654) — 3 hygiene + 1 Waze bonus ← cette release Test BUILD SUCCESSFUL, lint 0 erreur sur les 4 .java touchés. ⚠️ Smoke test device requis : focus sur lancement Waze sur cluster avec insets per-app sauvegardés (le path bonus #27 doit retry et applique enfin le resize). Branche : beta/1.2.0-dilink5 · Commit : 8196654 LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.28DashCast v1.2.28 (beta pre-release) Branch: beta/1.2.0-dilink5 · Not merged into main · versionCode 224 This pre-release bundles the cumulative work shipped on the DiLink 5 beta branch since v1.2.27 was first cut (build 218 → 224 inclusive). The car-side updater will pick it up as an upgrade strictly via versionCode 224 (any installed build with a lower versionCode triggers the update prompt). ⚠️ This is a beta pre-release. Field-tested on a single DL5 vehicle (Yandex Maps cluster routing). DL3 (Seal EU), DL4 (BYD-AUTO/DiLink4.0) and DL2 paths are preserved byte-for-byte through dynamic platform detection — every new DL5 code path is gated by Platform.isDiLink5(ctx) so non-DL5 cars are not affected. Highlights DL5 cluster app resize — full root-cause fix (4 layers) The cluster-app resize sliders (Adjust H / Adjust V in MainActivity, and per-app auto-apply on launch) were silently no-op'ing on DL5 since the platform was introduced. Four cascading bugs were diagnosed from four successive field logs, each fix peeling back the next layer: Fix #1 — am start --windowingMode 5 (FREEFORM). Without an explicit windowing mode the launched task lands in WINDOWING_MODE_FULLSCREEN, and cmd activity task resize is a silent no-op on fullscreen tasks since API 30+. Adding the AOSP am start --windowingMode 5 flag is now the prerequisite for any downstream resize. Fix #2 — bounds expressed in display-3 framebuffer space. The DL5 cluster topology uses a 1920×1080 shadow framebuffer on display 3 (XDJAScreenProjection_0), which Qt then scales down to the 1920×720 physical cluster dalle at composition time. The previous code computed bounds against the dalle's 720 height (via mInputForwarder.getClusterHeight()), which shrunk the task inside an already-larger buffer. resizeActiveTask() now queries DisplayManager.getDisplay(clusterId).getRealSize() to express bounds in the task's actual display space, with a mInputForwarder fallback that preserves DL3 behaviour. Fix #3 — post-resize VERIFY probe. After every successful cmd activity task resize, a stanza-targeted dumpsys activity activities is dispatched to confirm the task ended up at the expected mBounds and mWindowingMode. Earlier versions used a noisy grep | head -20 that truncated before reaching the task's actual stanza; v1.2.28 uses an awk range /Task=Task\{[^}]*#N[ }]/,/Task=Task\{[^}]*#[0-9]+[ }]/ to extract just the task's stanza, then greps only the relevant fields (mBounds, WindowingMode, displayId, resizeMode). Fix #4 — force_resizable_activities + am force-stop chain. Even after Fix #1+#2+#3 landed, field logs showed the activity being coerced to mWindowingMode=fullscreen immediately after launch. Root cause: most navigation apps (Yandex Maps, Yandex Navi, Google Maps, Sygic, …) declare android:resizeableActivity="false" in their manifest to block split-screen. API 32 honors this flag strictly: non-resizable activities cannot enter freeform, regardless of the parent root task's windowing mode. The standard Android developer-option override is Settings.Global.force_resizable_activities=1 (visible as "Force activities to be resizable" in Developer Options). DashCast now sets this flag idempotently via the local ADB shell pipe (uid 2000 holds WRITE_SECURE_SETTINGS) before every cluster launch, and am force-stops the target package so its new process picks up the override at task creation time. The setting is persistent; re-applying every launch is self-healing. DL4 (BYD-AUTO/DiLink4.0) auto-detection — neutralises mis-applied DL5 toggle A new DL4 testeur reported "Service auto_container does not exist" 11+ times in production logs. Root cause: their car is a genuine DiLink 4.0 (Build.MODEL=DiLink4.0 For BYD AUTO, API 29), but the "DiLink 5" toggle in Settings had been manually flipped FORCE_ON. With FORCE_ON, the platform layer routed every service call through the DL5 snake_case binder auto_container, which doesn't exist on DL4 (DL4 uses PascalCase AutoContainer, like DL3). New Platform.detectDiLink4() auto-recognises DL4 from fingerprint/product/model (dilink4 / dilink_4 / dilink 4 substring match), gated by API 28-29 to prevent future ROM mis-routing. New Platform.isDiLink4(Context) accessor (read-only, no user override — DL4 detection is purely automatic, mirroring isDiLink2). Platform.isDiLink5(Context) is hard-neutralised on DL4 hardware: returns false unconditionally before reading the override, with describeMode() reporting AUTO=off (DL4 detected — DL5 FORCE_ON ignored) so the Settings panel surfaces the absorbed mistake to power users. Downstream code is auto-rerouted with zero changes: AdbLocalClient.autoContainerSvcName falls back to AutoContainer, ClusterManager.activateClusterDisplay falls back to the DL3 path (sendInfo 30 → 6 s → 16 → 6 s → 35 → poll DisplayManager). DL4 Diagnostic tab — 40 reconnaissance + live tests A new "DiLink 4" tab appears in DiagActivity between "DiLink 2" and "Mirror", auto-selected when Platform.isAutoDetectedDiLink4() is true. 40 tests across three tiers: L1–L15 (recon): platform fingerprint, AutoContainer binder reachability, DisplayManager inventory, SurfaceFlinger probe, BYD/XDJA package enumeration, ServiceManager filter, IAutoContainer Stub class probe, /sys/class/drm framebuffer detection, 20 candidate service names brute probe, 25 BYD-specific properties, granted system permissions audit, ADB 5555 socket probe, launchable BYD apps + com.byd.clusterdebug presence. S1–S15 (shell): shell smoke id -u, getprop filter, wm size/density/overscan health, dumpsys display, dumpsys SurfaceFlinger --display-id, shell-side service list, service call AutoContainer PascalCase probe, service call auto_container snake_case negative probe (PASS = correctly absent on DL4), pm list packages -f, ResumedActivity snapshot, getenforce + id -Z, cluster/fission process filter, BYD-filtered settings, am start --display 0 smoke, hardware fingerprint. D1–D10 (destructive, live cluster activation): sendInfo(1000,30,"") prep → 6 s wait + DisplayManager dump → sendInfo(1000,16,"") projection open → 6 s wait + dump → sendInfo(1000,35,"") cluster size 10.25" → 6 s wait + dump + cluster display discovery → am start --display N against com.byd.clusterdebug → 2.5 s wait + dump confirms target landed → cleanup (am force-stop + sendInfo(1000,18,"")) → final sendInfo(1000,0,"") restore + 2 s wait. "Run all" + "Copy report" buttons let DL4 users dump a plain-text report (Build identity + 40 results) for forward debug. i18n — 100% coverage across 12 locales (fr, en, ar, be, de, es, it, kk, ru, tr, uk, uz) 342 string keys × 12 locales = 4104 translated entries. Final passes shipped between builds 220 and 222: Phase 4 (46 keys): ADAS / Sniffer diagnostic panels, runtime Sniffer status formats, 6 Sniffer toasts, cleanup dialog, chooser title, Settings warnings (DL2 no-margins, no cluster, error prefix, no browser). Phase 5 (4 keys): 3 long technical doc cards in include_diag_adas.xml / include_diag_sniffer.xml (previously left FR because of mixed prose + code identifiers + Chinese ROM literals), plus the IME-bridge a11y success toast ("DashCast Cluster IME ✓") that was inlined in Java. Technical invariants (binder names, Chinese ROM resource literals, version fingerprints, brand strings, single-letter symbols) are intentionally not translated. Diagnostic-tier changes (DL5) DL5 KeyboardBridge — pivot to AccessibilityNodeInfo.ACTION_SET_TEXT (replaces brittle per-character KeyEvent injection). Cross-display-native, immune to per-display IME isolation and fission compositor asymmetry. Bridge window is now floating 220×48dp with deferred showSoftInput via onWindowFocusChanged, fixes the "Ignoring showSoftInput as view is not served" warning and the head-unit overlay covering cluster mirror. DL5 KeyboardBridge — auto-enable a11y service via local ADB. settings put secure enabled_accessibility_services with colon-list preservation (no clobbering TalkBack or BYD a11y services). Single-tap to enable, no Settings round-trip needed. DL5 KeyboardBridge — ANR fix. ClusterImeWatcherService.setTextOnCluster / performImeEnterOnCluster moved off the UI thread onto a dedicated HandlerThread cluster-ime-relay, with 80ms debounce coalescing — fast typists no longer ANR the bridge. DL5 KeyboardBridge — cross-display window walk. findClusterFocusedEditable now uses AccessibilityService.getWindowsOnAllDisplays() (API 30+) instead of getWindows() (default-display-only), with explicit self-package skip so the bridge's own EditText doesn't compete for findFocus(FOCUS_INPUT) and steal keystrokes back to the head unit. DL5 IME a11y banner — one-click activation via daemon shell (uid=2000 / WRITE_SECURE_SETTINGS), 3-tier Settings fallback (canonical AOSP action → BYD-specific BYD_ACCESSIBILITY action → explicit com.byd.carsettings/.AccessibilityMainActivity component → generic Settings.ACTION_SETTINGS). DL5 per-app resize — cmd activity task resize direct shell dispatch (skips am task resize which is a silent no-op on API 30+). DL5 task lookup — daemon dumpsys activity recents fallback (the default ActivityManager.getRunningTasks(50) returns only the caller's own task for non-system apps on API 21+; the daemon shell uid 2000 sees the full cross-package recents table). Diag → Sniffer — "Nettoyer" button restored (lost in the v0.9.88 M3 redesign). Refuses to run while a capture is active, AlertDialog confirms scope, deletes byd_log_*.log / byd_report_*.txt / BYD_RE_Sniffer_*.txt from getExternalFilesDir(null) + getFilesDir() plus cluster_live.png from getExternalCacheDir(), reports remaining bytes. Compatibility matrix Platform Auto-detected via Behavioural change in v1.2.28 DL5 Platform.isDiLink5(ctx) (manual toggle or build signature) All 4 resize fixes, all DL5 KeyboardBridge fixes, IME a11y banner, cmd activity task resize direct dispatch, daemon dumpsys fallback for task lookup. DL4 Build.MODEL / ro.product.name substring dilink4 + API 28–29 New: auto-detection, hard-neutralisation of DL5 FORCE_ON, new Diag tab (40 tests). No regression on existing DL4 paths. DL3 (Seal EU) API 29 + non-DL4 fingerprint Zero behavioural change. New code paths gated upstream. DL2 Single HMI (no cluster) Zero behavioural change. Cluster paths inert as before. Internal build cadence (English summary, 218 → 224) versionCode Internal tag Summary 224 1.2.28-build224 Public pre-release — bumps versionName to 1.2.28, no code change vs build 223. 223 1.2.32-build223 DL5 resize Fix #4: force_resizable_activities + am force-stop chain, VERIFY probe stanza-targeted. 222 1.2.31-build222 i18n 100% — 4 missing keys × 12 locales (ADAS / Sniffer doc cards + IME-bridge toast). 221 1.2.30-build221 DL5 resize Fixes #1+#2+#3: --windowingMode 5 + framebuffer-space bounds + VERIFY probe. 220 1.2.29-build220 i18n Phase 4 — 46 keys × 12 locales (ADAS / Sniffer panels + Toasts + Settings warnings). 219 1.2.28-build219 DiagActivity DiLink 4 tab — 40 tests (L1-15 recon, S1-15 shell, D1-10 live cluster activation). 218 1.2.27-build218 DL4 auto-detection + hard-neutralisation of mis-applied DL5 FORCE_ON. Install instructions The APK is signed with the project debug key. On a BYD car with a previous DashCast install (versionCode < 224), the in-app updater will detect the upgrade automatically. For a manual install: download DashCast-v1.2.28-debug.apk below and adb install -r <path> (or sideload via the car's file manager). To roll back to 1.2.27 in case of regression, uninstall first (adb uninstall com.byd.dashcast), then install the 1.2.27 APK — Android refuses downgrades by versionCode otherwise. LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.27DashCast v1.2.27 (pre-release) Public versionName: 1.2.27 · versionCode: 222 · Track: beta/1.2.0-dilink5 Pre-release published from the beta/1.2.0-dilink5 branch. Not merged into main. The car-side updater detects this APK as an upgrade because versionCode 222 > 220 (last shipped to field). The public versionName is intentionally kept at 1.2.27 to stay aligned with the announced 1.2.x cadence; build identity is tracked via versionCode and the CHANGELOG below. Highlights since v1.2.26 (build 217) This pre-release rolls up five internal builds (218 → 222), covering one new hardware target (DiLink 4), one full diagnostic suite, the new field-log sniffer panel, a finished i18n campaign at 100% coverage, and — most importantly for current DL5 users — a root-cause fix for the cluster resize regression that has been silently swallowing every margin setting on DiLink 5 hardware. Build 222 — versionName 1.2.31 — i18n 100% (ADAS / Sniffer panels + Toasts) Cross-cutting i18n audit. All Toast.makeText call sites across every Activity were verified — 14 of 15 were already wired to R.string.*; the remaining literal ("DashCast Cluster IME ✓" inside the success branch of KeyboardBridgeActivity.tryAdbEnableA11y) was promoted to R.string.keyboard_bridge_active_toast. All 9 diagnostic layouts (activity_diag.xml + the 8 include_diag_*.xml) were grep-audited for raw android:text values. Three documentation cards were still leaking French prose because their content mixes translatable text with non-translatable code identifiers, shell commands, and Chinese CJK literals: include_diag_adas.xml:188 — Auto_container service doc (PascalCase vs snake_case naming, codes 12/13, infoListInit variants per DiLink generation, R-series unsupported) → diag_adas_doc_help include_diag_sniffer.xml:183 — captured content description (header, live logcat threadtime, auto/manual snapshots, setsid persistence) → diag_sniffer_doc_content include_diag_sniffer.xml:220 — 5-step user workflow (Start → Reproduce → Snapshot → Stop → Export) → diag_sniffer_doc_workflow Four new keys were translated manually (no machine pseudo-translation) and added to all 12 supported locales: ar, be, de, en, es, fr (default), it, kk, ru, tr, uk, uz. Code identifiers (Auto_container, infoListInit, infoListInit_Di5, SecondActivity, clusterdebug, setsid, ro.product.model, dumpsys SurfaceFlinger, logcat threadtime, etc.) and CJK literals (显示Adas, 关闭Adas — the actual on-device resource labels in the BydDashboard APK) are preserved verbatim across every locale because translating them would be a bug. Total final coverage: 342 string keys × 12 locales = 4 104 translated entries. Identified but deliberately left untranslated as a design decision: adapter placeholders (row_log.xml "INFO" / "00:00:00" / "Tag" / "message", item_beta_test.xml "Title" / "Description" / "X0" — all overwritten at bind time), Unicode symbols (⚡, ✕, ✓, ➖, ➕, ⊞, ▼, ↺), universal units ("50 px" / "80 px" — SeekBar default placeholders replaced at runtime), brand and URLs ("DashCast Cluster IME", "RE Sniffer", "github.com/Kiroha/byd-dashcast"), and binder service identifiers ("Auto_container" as a single-word card label). Build 221 — versionName 1.2.30 — DL5 cluster resize root-cause fix (three cascading patches in ClusterService.java) The DL5 cluster resize regression that was silently absorbing every user-defined margin on DiLink 5 hardware has been root-caused and fixed. Diagnosis was triggered by field log log/BYD_RE_Sniffer_20260523_184727.txt from a DL5 user running Yandex Maps on build 219: the log confirms the app is correctly launched on display 3 (XDJAScreenProjection_0, taskId=49 verified), cmd activity task resize 49 374 275 1546 445 deterministically returns exit=0… yet the cluster face (physical display 2, 1920×720) remains full-bleed with no visible margin whatsoever. Three tightly interlocked causes were identified and fixed together. Fix #1 — startActivityViaShell() now passes --windowingMode 5 (FREEFORM) The shell command was previously am start --display 3 -a MAIN -c LAUNCHER -n pkg/cls --activity-clear-task with no explicit windowing mode flag. As a result the task landed in WINDOWING_MODE_FULLSCREEN (=1). On API 30+, cmd activity task resize is a silent no-op against fullscreen tasks — the internal AOSP path ActivityTaskManagerService.resizeTask detects fullscreen and bails without raising any error, returning exit=0. Adding --windowingMode 5 (the official AOSP am start flag, available since API 26 and still present in API 32) between --display N and -a MAIN makes the task land directly in FREEFORM mode, where bounds resize is honored. Note: applyClusterFreeformBounds() was already computing ActivityOptions.setLaunchWindowingMode(5) + setLaunchBounds(…) correctly, but on DL5 those ActivityOptions were silently discarded because startActivityViaShell() replaces startActivityViaIAM() (which raises SecurityException uid 10148 for cross-display launches on DL5). Fix #2 — resizeActiveTask() now expresses bounds in display-3 framebuffer space The field log revealed a coordinate-space mismatch: at launch time applyClusterFreeformBounds correctly logs cluster FREEFORM bounds=Rect(374, 275 - 1546, 805) display=3 1920×1080 (computed via Display.getRealSize() on display 3 → height 1080), but the subsequent resize logs bounds=Rect(374, 275 - 1546, 445) (height 720, computed from the hard-coded ClusterInputForwarder.mClusterHeight). Two different coordinate spaces — incoherent results. The task actually lives inside the display-3 framebuffer (1920×1080), not in the physical cluster face space (1920×720) — Qt-side native code downscales 1080 → 720 onto the dalle via the Surface consumed by the XDJA TextureView (pipeline confirmed by RE: SecondaryDisplayService.addView(TextureView) on display 2 → onSurfaceTextureAvailable → transact(2, surface) → native createVirtualDisplay("remote_dashboard", 1920, 720, 320, surface, 320) → DISPLAY_SECONDARY_ID = 3 → am start --display 3). resizeTask applies bounds in the task's own display space, so bounds must be in display-3 dimensions (1920×1080) — otherwise we shrink the task inside an already-larger buffer. The code now queries DisplayManager.getDisplay(clusterId).getRealSize() directly at resize time, with fallback to mInputForwarder.getClusterWidth/Height() on detection failure (preserves DL3 behavior, where display 1 IS the dalle and both paths return 1920×720). On DL3 this is a no-op equivalence. Consequence: user-entered H/V margins are interpreted in framebuffer space; on DL5 they will be very slightly compressed vertically by Qt (factor 720/1080 ≈ 0.667 on V), but visually consistent. Fix #3 — Post-resize verification probe (dumpsys activity activities) After cmd activity task resize returns success, the code now chains a tightly filtered dumpsys activity activities | grep -E 'taskId=N|Task=…#N|mBounds|getWindowingMode|windowingMode=' shell call. The result is logged at INFO with the prefix resizeActiveTask VERIFY. This allows field-log analysis to confirm that (a) the task is genuinely in windowingMode=5 (FREEFORM) after our --windowingMode 5 launch flag, and (b) mBounds exactly matches the Rect(insetH, insetV, fbW - insetH, fbH - insetV) we requested. If both invariants hold but nothing changes visually, the next investigation will focus on the Qt/XDJA layer (the TextureView consuming the display-3 Surface may filter or stretch differently). Cross-platform compatibility DL3 (Seal EU, API 29): (i) Fix #1 is inactive — DL3 uses startActivityViaIAM(), not the shell fallback; (ii) Fix #2 is transparent — getRealSize() on display 1 returns 1920×720, identical to getClusterHeight(); (iii) Fix #3 is inactive — DL3 uses the legacy else branch with am task resize. DL2 (HMI only, no cluster display): clusterId<=0 → method bails upfront, no path touched. DL4: same code path as DL3 if platform auto-detection routes through IATM. Zero regression risk on non-DL5 platforms. Build 220 — versionName 1.2.29 — i18n campaign Phase 4 (DiagActivity + SettingsActivity, 46 keys × 12 locales) Translation campaign completion phase. 46 previously-French-only strings spanning DiagActivity, SettingsActivity and the diagnostic layouts (include_diag_adas.xml, include_diag_sniffer.xml, activity_diag.xml) were promoted to R.string.* and translated across all 12 locales. Coverage went from partial (≈70% on diag screens) to 100%. Zero regression on existing UI; one new Spanish translation polish. Build 219 — versionName 1.2.28 — DiLink 4 diagnostic tab in DiagActivity (40 tests) Companion of build 218 (DL4 platform detection). The user now needs a way to validate end-to-end that the DL3-style activation sequence (sendInfo(1000, 16) open / sendInfo(1000, 18) close / sendInfo(1000, 0) restore) is being correctly applied on DL4 hardware after the DL5 override neutralisation. Full DL4 panel added: 40 grouped tests covering L1–L15 (recon: package presence, service binder probes, manifest provider scans, framebuffer dumpsys snapshots), S1–S15 (shell: am start --display N per non-main display, dumpsys SurfaceFlinger, dumpsys display, framebuffer state pre/post sendInfo, force-stop sequences), and D1–D10 (live cluster activation probes: open projection → wait → spawn canary on display N → wait → retract → dump activities). All wired to AdbLocalClient with per-test result rows, copy-to-clipboard, and a one-tap "Run all" button. Pure additive: zero impact on existing DiagActivity tabs (DL3, DL5, Mirror, etc.). Untracked DL4 source files were not included in this pre-release (still on the testeur's side). Build 218 — versionName 1.2.27 — DiLink 4 auto-detection + FORCE_ON DL5 override neutralisation Field log BYD_RE_Sniffer_20260523_175751.txt from a new testeur on DiLink 4 hardware (BYD-AUTO/DiLink4.0, API 29) proved the cluster never activated: every projection attempt logged Service auto_container does not exist (snake_case spelling, repeated 11+ times across log/byd_log_20260523_*.txt). Root cause: the build had been hardcoded with a FORCE_ON DiLink 5 debug override that forced every device through the DL5 codepath, including the auto_container service binding which doesn't exist on DL4 (DL4 uses Auto_container in PascalCase, matching DL3). Platform auto-detection was added in Platform.java based on ro.build.product + ro.product.brand + API level matrix (DL3 = Seal* API 29, DL4 = BYD-AUTO/DiLink4.0 API 29, DL5 = BYD-AUTO/DiLink5.x API 32). The FORCE_ON override is hard-neutralised on non-DL5 hardware. DL4 now correctly routes to the PascalCase Auto_container service and the DL3-equivalent sendInfo sequence, restoring cluster activation on the new testeur's car. Compatibility & risk DL3 (Seal EU, API 29) — Pure additive. All DL3 codepaths unchanged: legacy resize chain (am task resize + cmd activity task resize fallback) preserved, startActivityViaIAM() still used (no SecurityException on DL3), getRealSize() on display 1 equals getClusterHeight() so Fix #2 is a no-op equivalence. DL4 (BYD-AUTO/DiLink4.0, API 29) — Now auto-detected since build 218. Routes through DL3-equivalent activation path. Resize fixes inactive (DL4 doesn't use the DL5 shell fallback). DL5 (BYD-AUTO/DiLink5.x, API 32) — Primary target of build 221 fixes. Worst case if Fix #1+#2 don't produce the expected visual effect = current state (silent resize no-op) plus the new VERIFY log from Fix #3 explaining why. Strict behavioral superset. DL2 (HMI only) — No cluster display → resizeActiveTask bails upfront → no codepath touched. What testers should look for After installing, switch the cluster app to anything you want to resize (e.g. Yandex Maps, Yandex Navi, any maps/media app), set H=100 / V=80 in the DashCast main settings, send it to the cluster, then capture a field log via Diag → Sniffer → Start → reproduce → Stop → Export. Grep for resizeActiveTask VERIFY in the captured BYD_RE_Sniffer_*.txt file: windowingMode=5 (FREEFORM) confirms Fix #1 took effect. mBounds=Rect(100, 80 - 1820, 1000) (approximately, depends on framebuffer size) confirms Fix #2 + Fix #1 cooperate correctly. If both are present but the cluster face still shows no margin: please share the field log, the investigation moves to the Qt/XDJA scaling layer. Files attached DashCast-v1.2.27-debug.apk — public versionName 1.2.27, internal versionCode 222. Git track Branch: beta/1.2.0-dilink5 Tag: v1.2.27 (internal cadence marker) Base: v1.2.26 (build 217) Not merged into main. LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.31-betaDashCast v1.2.31-beta (pre-release) Public versionName: 1.2.27 · versionCode: 222 · Track: beta/1.2.0-dilink5 Pre-release published from the beta/1.2.0-dilink5 branch. Not merged into main. The car-side updater detects this APK as an upgrade because versionCode 222 > 220 (last shipped to field). The public versionName is intentionally kept at 1.2.27 to stay aligned with the announced 1.2.x cadence; build identity is tracked via versionCode and the CHANGELOG below. Highlights since v1.2.26 (build 217) This pre-release rolls up five internal builds (218 → 222), covering one new hardware target (DiLink 4), one full diagnostic suite, the new field-log sniffer panel, a finished i18n campaign at 100% coverage, and — most importantly for current DL5 users — a root-cause fix for the cluster resize regression that has been silently swallowing every margin setting on DiLink 5 hardware. Build 222 — versionName 1.2.31 — i18n 100% (ADAS / Sniffer panels + Toasts) Cross-cutting i18n audit. All Toast.makeText call sites across every Activity were verified — 14 of 15 were already wired to R.string.*; the remaining literal ("DashCast Cluster IME ✓" inside the success branch of KeyboardBridgeActivity.tryAdbEnableA11y) was promoted to R.string.keyboard_bridge_active_toast. All 9 diagnostic layouts (activity_diag.xml + the 8 include_diag_*.xml) were grep-audited for raw android:text values. Three documentation cards were still leaking French prose because their content mixes translatable text with non-translatable code identifiers, shell commands, and Chinese CJK literals: include_diag_adas.xml:188 — Auto_container service doc (PascalCase vs snake_case naming, codes 12/13, infoListInit variants per DiLink generation, R-series unsupported) → diag_adas_doc_help include_diag_sniffer.xml:183 — captured content description (header, live logcat threadtime, auto/manual snapshots, setsid persistence) → diag_sniffer_doc_content include_diag_sniffer.xml:220 — 5-step user workflow (Start → Reproduce → Snapshot → Stop → Export) → diag_sniffer_doc_workflow Four new keys were translated manually (no machine pseudo-translation) and added to all 12 supported locales: ar, be, de, en, es, fr (default), it, kk, ru, tr, uk, uz. Code identifiers (Auto_container, infoListInit, infoListInit_Di5, SecondActivity, clusterdebug, setsid, ro.product.model, dumpsys SurfaceFlinger, logcat threadtime, etc.) and CJK literals (显示Adas, 关闭Adas — the actual on-device resource labels in the BydDashboard APK) are preserved verbatim across every locale because translating them would be a bug. Total final coverage: 342 string keys × 12 locales = 4 104 translated entries. Identified but deliberately left untranslated as a design decision: adapter placeholders (row_log.xml "INFO" / "00:00:00" / "Tag" / "message", item_beta_test.xml "Title" / "Description" / "X0" — all overwritten at bind time), Unicode symbols (⚡, ✕, ✓, ➖, ➕, ⊞, ▼, ↺), universal units ("50 px" / "80 px" — SeekBar default placeholders replaced at runtime), brand and URLs ("DashCast Cluster IME", "RE Sniffer", "github.com/Kiroha/byd-dashcast"), and binder service identifiers ("Auto_container" as a single-word card label). Build 221 — versionName 1.2.30 — DL5 cluster resize root-cause fix (three cascading patches in ClusterService.java) The DL5 cluster resize regression that was silently absorbing every user-defined margin on DiLink 5 hardware has been root-caused and fixed. Diagnosis was triggered by field log log/BYD_RE_Sniffer_20260523_184727.txt from a DL5 user running Yandex Maps on build 219: the log confirms the app is correctly launched on display 3 (XDJAScreenProjection_0, taskId=49 verified), cmd activity task resize 49 374 275 1546 445 deterministically returns exit=0… yet the cluster face (physical display 2, 1920×720) remains full-bleed with no visible margin whatsoever. Three tightly interlocked causes were identified and fixed together. Fix #1 — startActivityViaShell() now passes --windowingMode 5 (FREEFORM) The shell command was previously am start --display 3 -a MAIN -c LAUNCHER -n pkg/cls --activity-clear-task with no explicit windowing mode flag. As a result the task landed in WINDOWING_MODE_FULLSCREEN (=1). On API 30+, cmd activity task resize is a silent no-op against fullscreen tasks — the internal AOSP path ActivityTaskManagerService.resizeTask detects fullscreen and bails without raising any error, returning exit=0. Adding --windowingMode 5 (the official AOSP am start flag, available since API 26 and still present in API 32) between --display N and -a MAIN makes the task land directly in FREEFORM mode, where bounds resize is honored. Note: applyClusterFreeformBounds() was already computing ActivityOptions.setLaunchWindowingMode(5) + setLaunchBounds(…) correctly, but on DL5 those ActivityOptions were silently discarded because startActivityViaShell() replaces startActivityViaIAM() (which raises SecurityException uid 10148 for cross-display launches on DL5). Fix #2 — resizeActiveTask() now expresses bounds in display-3 framebuffer space The field log revealed a coordinate-space mismatch: at launch time applyClusterFreeformBounds correctly logs cluster FREEFORM bounds=Rect(374, 275 - 1546, 805) display=3 1920×1080 (computed via Display.getRealSize() on display 3 → height 1080), but the subsequent resize logs bounds=Rect(374, 275 - 1546, 445) (height 720, computed from the hard-coded ClusterInputForwarder.mClusterHeight). Two different coordinate spaces — incoherent results. The task actually lives inside the display-3 framebuffer (1920×1080), not in the physical cluster face space (1920×720) — Qt-side native code downscales 1080 → 720 onto the dalle via the Surface consumed by the XDJA TextureView (pipeline confirmed by RE: SecondaryDisplayService.addView(TextureView) on display 2 → onSurfaceTextureAvailable → transact(2, surface) → native createVirtualDisplay("remote_dashboard", 1920, 720, 320, surface, 320) → DISPLAY_SECONDARY_ID = 3 → am start --display 3). resizeTask applies bounds in the task's own display space, so bounds must be in display-3 dimensions (1920×1080) — otherwise we shrink the task inside an already-larger buffer. The code now queries DisplayManager.getDisplay(clusterId).getRealSize() directly at resize time, with fallback to mInputForwarder.getClusterWidth/Height() on detection failure (preserves DL3 behavior, where display 1 IS the dalle and both paths return 1920×720). On DL3 this is a no-op equivalence. Consequence: user-entered H/V margins are interpreted in framebuffer space; on DL5 they will be very slightly compressed vertically by Qt (factor 720/1080 ≈ 0.667 on V), but visually consistent. Fix #3 — Post-resize verification probe (dumpsys activity activities) After cmd activity task resize returns success, the code now chains a tightly filtered dumpsys activity activities | grep -E 'taskId=N|Task=…#N|mBounds|getWindowingMode|windowingMode=' shell call. The result is logged at INFO with the prefix resizeActiveTask VERIFY. This allows field-log analysis to confirm that (a) the task is genuinely in windowingMode=5 (FREEFORM) after our --windowingMode 5 launch flag, and (b) mBounds exactly matches the Rect(insetH, insetV, fbW - insetH, fbH - insetV) we requested. If both invariants hold but nothing changes visually, the next investigation will focus on the Qt/XDJA layer (the TextureView consuming the display-3 Surface may filter or stretch differently). Cross-platform compatibility DL3 (Seal EU, API 29): (i) Fix #1 is inactive — DL3 uses startActivityViaIAM(), not the shell fallback; (ii) Fix #2 is transparent — getRealSize() on display 1 returns 1920×720, identical to getClusterHeight(); (iii) Fix #3 is inactive — DL3 uses the legacy else branch with am task resize. DL2 (HMI only, no cluster display): clusterId<=0 → method bails upfront, no path touched. DL4: same code path as DL3 if platform auto-detection routes through IATM. Zero regression risk on non-DL5 platforms. Build 220 — versionName 1.2.29 — i18n campaign Phase 4 (DiagActivity + SettingsActivity, 46 keys × 12 locales) Translation campaign completion phase. 46 previously-French-only strings spanning DiagActivity, SettingsActivity and the diagnostic layouts (include_diag_adas.xml, include_diag_sniffer.xml, activity_diag.xml) were promoted to R.string.* and translated across all 12 locales. Coverage went from partial (≈70% on diag screens) to 100%. Zero regression on existing UI; one new Spanish translation polish. Build 219 — versionName 1.2.28 — DiLink 4 diagnostic tab in DiagActivity (40 tests) Companion of build 218 (DL4 platform detection). The user now needs a way to validate end-to-end that the DL3-style activation sequence (sendInfo(1000, 16) open / sendInfo(1000, 18) close / sendInfo(1000, 0) restore) is being correctly applied on DL4 hardware after the DL5 override neutralisation. Full DL4 panel added: 40 grouped tests covering L1–L15 (recon: package presence, service binder probes, manifest provider scans, framebuffer dumpsys snapshots), S1–S15 (shell: am start --display N per non-main display, dumpsys SurfaceFlinger, dumpsys display, framebuffer state pre/post sendInfo, force-stop sequences), and D1–D10 (live cluster activation probes: open projection → wait → spawn canary on display N → wait → retract → dump activities). All wired to AdbLocalClient with per-test result rows, copy-to-clipboard, and a one-tap "Run all" button. Pure additive: zero impact on existing DiagActivity tabs (DL3, DL5, Mirror, etc.). Untracked DL4 source files were not included in this pre-release (still on the testeur's side). Build 218 — versionName 1.2.27 — DiLink 4 auto-detection + FORCE_ON DL5 override neutralisation Field log BYD_RE_Sniffer_20260523_175751.txt from a new testeur on DiLink 4 hardware (BYD-AUTO/DiLink4.0, API 29) proved the cluster never activated: every projection attempt logged Service auto_container does not exist (snake_case spelling, repeated 11+ times across log/byd_log_20260523_*.txt). Root cause: the build had been hardcoded with a FORCE_ON DiLink 5 debug override that forced every device through the DL5 codepath, including the auto_container service binding which doesn't exist on DL4 (DL4 uses Auto_container in PascalCase, matching DL3). Platform auto-detection was added in Platform.java based on ro.build.product + ro.product.brand + API level matrix (DL3 = Seal* API 29, DL4 = BYD-AUTO/DiLink4.0 API 29, DL5 = BYD-AUTO/DiLink5.x API 32). The FORCE_ON override is hard-neutralised on non-DL5 hardware. DL4 now correctly routes to the PascalCase Auto_container service and the DL3-equivalent sendInfo sequence, restoring cluster activation on the new testeur's car. Compatibility & risk DL3 (Seal EU, API 29) — Pure additive. All DL3 codepaths unchanged: legacy resize chain (am task resize + cmd activity task resize fallback) preserved, startActivityViaIAM() still used (no SecurityException on DL3), getRealSize() on display 1 equals getClusterHeight() so Fix #2 is a no-op equivalence. DL4 (BYD-AUTO/DiLink4.0, API 29) — Now auto-detected since build 218. Routes through DL3-equivalent activation path. Resize fixes inactive (DL4 doesn't use the DL5 shell fallback). DL5 (BYD-AUTO/DiLink5.x, API 32) — Primary target of build 221 fixes. Worst case if Fix #1+#2 don't produce the expected visual effect = current state (silent resize no-op) plus the new VERIFY log from Fix #3 explaining why. Strict behavioral superset. DL2 (HMI only) — No cluster display → resizeActiveTask bails upfront → no codepath touched. What testers should look for After installing, switch the cluster app to anything you want to resize (e.g. Yandex Maps, Yandex Navi, any maps/media app), set H=100 / V=80 in the DashCast main settings, send it to the cluster, then capture a field log via Diag → Sniffer → Start → reproduce → Stop → Export. Grep for resizeActiveTask VERIFY in the captured BYD_RE_Sniffer_*.txt file: windowingMode=5 (FREEFORM) confirms Fix #1 took effect. mBounds=Rect(100, 80 - 1820, 1000) (approximately, depends on framebuffer size) confirms Fix #2 + Fix #1 cooperate correctly. If both are present but the cluster face still shows no margin: please share the field log, the investigation moves to the Qt/XDJA scaling layer. Files attached DashCast-v1.2.27-debug.apk — public versionName 1.2.27, internal versionCode 222. Git track Branch: beta/1.2.0-dilink5 Tag: v1.2.31-beta (internal cadence marker) Base: v1.2.26 (build 217) Not merged into main. LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.26DashCast v1.2.26 — Pre-release (beta/1.2.0-dilink5) Branch: beta/1.2.0-dilink5 · versionCode: 217 · versionName: 1.2.26 APK: DashCast-v1.2.26-debug.apk (drop-in update over v1.2.25) Pre-release on the DiLink 5 development line. Not merged into main. Production DL3 (Seal EU) users should keep using the latest 1.0.x stable release. What this fixes v1.2.25 nailed the keyboard end-to-end (user confirmed « ENFIN ! the keyboard works, it sends the text correctly »). Same field session surfaced one remaining bug: the per-app resize / overscan controls in MainActivity have been a silent no-op on DL5 all along. Root cause Field log BYD_RE_Sniffer_20260523_172007.txt shows the exact pattern on every press of Apply Resize on Yandex Maps (taskId=38), repeated 11 times: 17:21:15.717 W ClusterService: resizeActiveTask reflection failed: SecurityException: Permission Denial: resizeTask() from pid=22748, uid=10148 requires android.permission.MANAGE_ACTIVITY_TASKS 17:21:15.758 D AdbLocalClient: am task resize 38 206 91 1714 629 2>&1; echo "exit=$?" -> exit=0 17:21:15.758 I ClusterService: resizeActiveTask `am task resize` -> "exit=0" (looksOk=true) The reflection path IActivityTaskManager.resizeTask() rightly fails with SecurityException (app uid 10148 lacks MANAGE_ACTIVITY_TASKS, which is signature|privileged). The shell fallback am task resize returns exit=0 deterministically on DL5 (API 32) — but the cluster doesn't move. v1.2.18 already commented this exact symptom (« am task resize returning empty stdout but no visible effect — on AOSP API 30+ the am verb was rewritten »). It added a cmd activity task resize fallback gated on a looksOk heuristic. On DL5 am returns exit=0 cleanly, looksOk=true, the fallback was never reached, ever. Fix ClusterService.resizeActiveTask now branches on AdbLocalClient.isDiLink5Safe(this): DL5 (API 32): skip am task resize entirely and dispatch cmd activity task resize <taskId> <l> <t> <r> <b> directly. This is the canonical AOSP API 30+ verb (modern replacement for am task), reached through the same uid=shell pipe that already executes pm grant, service call AutoContainer, settings put secure …. DL3 (API 29): preserve the legacy am task resize → fallback chain verbatim — am task resize is healthy on Android 10. Behavioural contract DL5: drag overscan sliders in MainActivity (or the global Settings sliders → auto-applied per-app via autoApplyInsetsIfNeeded) → press Apply → cmd activity task resize fires → cluster app shrinks/expands within the safe area in ≤ 1 frame. DL3: zero behavioural change. DL2: cluster path doesn't exist (zero impact). Touched files app/build.gradle — 216 → 217, 1.2.25 → 1.2.26 app/src/main/java/com/byd/dashcast/ClusterService.java — ~30 LoC in resizeActiveTask: DL5 branch dispatches cmd activity task resize directly; legacy DL3 chain preserved in else. Inline comment expanded to document the v1.2.26 field-log evidence. CHANGELOG.md — v1.2.26-build217 entry prepended. No new permissions, no manifest change, no new strings, no new dependency, no protocol bump. Install Drop-in update over v1.2.25 — Android Package Installer accepts the upgrade because versionCode is now 217. adb install -r DashCast-v1.2.26-debug.apk Validation plan DL5 testeur — install v1.2.26 over v1.2.25, launch Yandex Maps on the cluster, open MainActivity, move H/V sliders to e.g. 200/100, press Apply → cluster Yandex visibly indents by 200 px H / 100 px V. logcat | grep ClusterService should show resizeActiveTask DL5: dispatching 'cmd activity task resize' (skipping 'am task resize' — known silent no-op on API 30+) then resizeActiveTask 'cmd activity task resize' -> "exit=0", with no further am task line per resize. Press Reset Insets → cluster Yandex returns to full 1920×720 within a frame. DL3 — typing app keyboard sliders still work as before (legacy am task resize path, unchanged). LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.25DashCast v1.2.25 — Pre-release (beta/1.2.0-dilink5) Branch: beta/1.2.0-dilink5 · versionCode: 216 · versionName: 1.2.25 APK: DashCast-v1.2.25-debug.apk (drop-in update over v1.2.24) Pre-release on the DiLink 5 development line. Not merged into main. Production DL3 (Seal EU) users should keep using the latest 1.0.x stable release. What this fixes v1.2.24 killed the ANR that was murdering the app on every fast type session, but field testing surfaced two follow-on bugs: The « DashCast Cluster IME ✓ » Toast appeared every time the user tapped the ⌨ icon, and forced them to tap twice before they could actually type. Typing in the bridge produced zero text on the cluster Yandex search field — the worker logged setTextOnCluster no-op: no focused editable on cluster on every keystroke. Bug 1 — Root cause tryAdbEnableA11y's success branch (introduced in v1.2.23) called finish() on the bridge after the OK:… reply, on the assumption that the user would retap ⌨ and the second bridge would find a freshly bound service. The field log BYD_RE_Sniffer_20260523_170436.txt proves AccessibilityManagerService rebinds ClusterImeWatcherService within ~30 ms of the settings put: 17:05:02.243 I ClusterImeWatcher: onServiceConnected — watching cluster focus events (DL5=true) 17:05:02.272 I KeyboardBridge: tryAdbEnableA11y SUCCESS: OK:com.byd.airconditioning/.gesture.AcGestureService:...:com.byd.dashcast/com.byd.dashcast.ime.ClusterImeWatcherService The bridge already has IME focus, the soft keyboard is up, the worker sInstance is alive — there is nothing to wait for. Finishing the bridge here was pure user friction. The « Toast every time » perception is because Android revokes a11y enablement on every package update (AccessibilityManagerService.onPackageUpdateFinished, standard security measure) — so after each install the secure setting really did contain no entry for our service. With finish() removed, the user sees the enable Toast exactly once after each install, then keeps typing in the same bridge instance from then on. Bug 2 — Root cause findClusterFocusedEditable() called AccessibilityService.getWindows() — which on every Android API returns only the windows on the default display, even with FLAG_RETRIEVE_INTERACTIVE_WINDOWS set. The cluster Yandex search EditText lives on display 2 (composed face) / display 3 (shadow framebuffer driven by the XDJA fission compositor), neither of which is the default. So the window walk found exactly zero candidates and the worker logged no focused editable on cluster on every keystroke. Fix KeyboardBridgeActivity.tryAdbEnableA11y no longer calls finish() in the success branch — the bridge stays open, the Toast confirms the enablement, the user keeps typing. ClusterImeWatcherService.findClusterFocusedEditable() rewritten around AccessibilityService.getWindowsOnAllDisplays() (API 30+, DL5 ships API 32). Returns SparseArray<List<AccessibilityWindowInfo>> keyed by displayId — exactly what we need to find the cluster windows. Falls back to the single-display walk on older APIs (DL3 stays gated by isDiLink5 upstream so this is just belt-and-braces). New private helper pickFocusedEditableFrom(window, selfPkg) factored out so the cross-display loop is readable, and which explicitly skips windows from our own package. This is necessary because v1.2.25 keeps the bridge open after ADB success: the bridge's own EditText would otherwise compete for findFocus(FOCUS_INPUT) and win on display 0, causing us to push the user's keystrokes back into the local field instead of the cluster Yandex search. Behavioural contract DL5 with a11y already enabled (steady-state after this release): tap ⌨ → bridge opens → IME pops up → no Toast → typing routes to cluster Yandex immediately. DL5 right after install: tap ⌨ → bridge opens → IME pops up → Toast ✓ (ADB enabled the service) → user keeps typing in the same bridge → routes immediately. DL3 (Seal EU production, 1.0.x stable): bridge entry gated by isDiLink5, zero behavioural change. DL2: no cluster path, zero impact. Touched files app/build.gradle — 215 → 216, 1.2.24 → 1.2.25 app/src/main/java/com/byd/dashcast/KeyboardBridgeActivity.java — ~5 LoC: dropped finish() from the ADB success branch, expanded the inline comment to document why the bridge must stay open. app/src/main/java/com/byd/dashcast/ime/ClusterImeWatcherService.java — ~110 LoC: rewrote findClusterFocusedEditable around getWindowsOnAllDisplays, extracted pickFocusedEditableFrom helper with self-package filter. CHANGELOG.md — 1.2.25-build216 entry on top. No new permissions, no manifest change, no new strings, no new dependency, no protocol bump. Install Drop-in update over v1.2.24 — Android Package Installer accepts the upgrade because versionCode is now 216. No uninstall required. adb install -r DashCast-v1.2.25-debug.apk Validation plan Install v1.2.25 over v1.2.24, tap ⌨ once → bridge opens + IME up + Toast ✓ once → type « test » → cluster Yandex search shows « test » within a frame → tap IME Enter → search executes. Tap ⌨ a second time later: bridge opens + IME up, no Toast, typing routes immediately. logcat | grep ClusterImeWatcher — setTextOnCluster no-op: no focused editable on cluster should be silent during normal typing; only emitted if the cluster app has no focused EditText (i.e. user opened the bridge with no search field in focus on the cluster). Hardware Back button mid-typing — instant response, no freeze (v1.2.24 guarantee preserved). Stop projection from MainActivity, relaunch another app on the cluster — mirror touch forwards correctly (v1.2.24 guarantee preserved). LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.24DashCast v1.2.24 — Pre-release (beta/1.2.0-dilink5) Branch: beta/1.2.0-dilink5 · versionCode: 215 · versionName: 1.2.24 APK: DashCast-v1.2.24-debug.apk (14 MB, debug-signed, drop-in update over v1.2.23) Pre-release on the DiLink 5 development line. Not merged into main. Production DL3 (Seal EU) users should keep using the latest 1.0.x stable release. What this fixes v1.2.23 finally got the ADB auto-enable path to work end-to-end on DL5 (the tryAdbEnableA11y SUCCESS Toast confirmed that AccessibilityManagerService rebound ClusterImeWatcherService on the next tick, with BYD's own a11y services correctly preserved in the colon-separated list). But field testing of v1.2.23 surfaced three user-visible regressions: Typed characters never reached the cluster Yandex search field. The hardware Back button was completely unresponsive for 17-18 seconds. After stopping projection and relaunching another app, mirror touch was broken. A single root cause explains all three symptoms. Smoking gun in the field log BYD_RE_Sniffer_20260523_164557.txt: 16:46:18.804 I KeyboardBridge: tryAdbEnableA11y SUCCESS: OK:com.byd.airconditioning/.gesture.AcGestureService:com.android.systemui/.custom.StatusBarAccessibilityService:com.byd.autovoice/.../SceneSayService:com.byd.dashcast/com.byd.dashcast.ime.ClusterImeWatcherService 16:46:18.791 I ClusterImeWatcher: onServiceConnected — watching cluster focus events (DL5=true) ... 16:46:36.233 I am_anr (991): [0,15738,com.byd.dashcast,...,Input dispatching timed out (KeyboardBridgeActivity (server) is not responding. Waited 5000ms for KeyEvent)] 16:46:47.206 E ActivityManager: ANR in com.byd.dashcast (com.byd.dashcast/.KeyboardBridgeActivity) 16:46:47.277 I am_proc_died: [0,15738,com.byd.dashcast,0,2] 16:46:47.350 F DEBUG: at com.byd.dashcast.ime.ClusterImeWatcherService.setTextOnCluster:256 Root cause ClusterImeWatcherService.setTextOnCluster() (introduced in v1.2.12) ran fully synchronously on the caller's thread, which on every keystroke was the UI thread (TextWatcher.afterTextChanged inside KeyboardBridgeActivity). Each call performed findClusterFocusedEditable(), which iterates getWindows() and runs root.findFocus(FOCUS_INPUT) against every interactive window — including the cluster windows on display 3 reached through cross-display Binder round-trips into AccessibilityManagerService. With FLAG_RETRIEVE_INTERACTIVE_WINDOWS and the XDJA fission compositor topology each walk takes dozens of milliseconds. A fast typist queued enough walks back-to-back on the UI thread to push the next KeyEvent dispatch past the 5 000 ms input-dispatch timeout → ANR → process killed → the 17-18 s the user spent tapping the Back button was the system-wide SIGQUIT dump + process restart window → after restart the daemon Binder was dropped, hence the attemptStartMirror : service non disponible repeats around 16:46:55. The three reported symptoms collapse to one root cause: the a11y tree walk was running on the UI thread. Fix ClusterImeWatcherService now owns a private HandlerThread "cluster-ime-relay" (BACKGROUND priority), started in onCreate and torn down via removeCallbacksAndMessages(null) + quitSafely() in onDestroy. setTextOnCluster(CharSequence) is strictly non-blocking. It stores the latest text in volatile mPendingText, removes any in-flight runnable, and reposts with 80 ms debounce (last-writer wins) so a typist filling « apple street » triggers the a11y tree walk + ACTION_SET_TEXT at most ~12 times per second. Returns true optimistically; runtime failures are logged inside the worker. performImeEnterOnCluster() also dispatches to the worker: cancels the debounced setText, flushes it inline so Enter sees the final text, then performs ACTION_IME_ENTER (API 30+) or the ACTION_CLICK fallback (API 28/29). The on-cluster typing UX is identical from the user's perspective (text appears in the cluster field as they type, Enter executes the search) — only the threading model changes. The UI thread is never blocked on a Binder round-trip again. Behavioural contract DL5 (this branch's target): typing in the bridge no longer blocks the UI thread → no ANR → Back stays responsive → process is not killed → mirror touch pipeline is preserved across the typing session. v1.2.23's ADB auto-enable + 5-intent Settings fallback kept as-is. DL3 (Seal EU production, 1.0.x stable): bridge entry point is gated by isDiLink5 (early-return), zero behavioural change. DL2: no cluster path, zero impact. Touched files app/build.gradle — 214 → 215, 1.2.23 → 1.2.24 app/src/main/java/com/byd/dashcast/ime/ClusterImeWatcherService.java — +mWorkerThread/mWorker/mPendingText/mSetTextRunner fields; onCreate spins up the worker; onDestroy tears it down; setTextOnCluster rewritten to non-blocking dispatch + debounce; performImeEnterOnCluster rewritten to worker-dispatched flush-then-enter (~90 LoC net). CHANGELOG.md — 1.2.24-build215 entry on top. No new permissions, no manifest change, no new strings, no new dependency, no protocol bump. Install Drop-in update over v1.2.23 — Android Package Installer accepts the upgrade because versionCode is now 215. No uninstall required. adb install -r DashCast-v1.2.24-debug.apk Validation plan Install over v1.2.23, tap ⌨ → bridge opens (a11y already enabled from v1.2.23, no Toast/ADB round-trip) → IME pops up. Type rapidly « apple street » → each character (debounced at 80 ms) appears in the cluster Yandex search field within a frame. Tap IME Enter → Yandex executes the search. Test the hardware Back button mid-typing — every press is acknowledged immediately, no 5-18 s freeze. logcat | grep com.byd.dashcast — no am_anr line, no ANR in com.byd.dashcast, same pid throughout the session. Stop projection from MainActivity, relaunch Yandex on the cluster — mirror touch forwards correctly (process did not die, daemon Binder still connected). LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.23DL5 — No more Settings round-trip to enable Cluster IME User reasonably asked: « On ne peut pas forcer l'activation de accessibilité pour Dashcast via ADB ? » — and yes, we already speak to the localhost ADB daemon (Dadb 127.0.0.1:5555) everywhere else in the app, so we now flip the secure flag directly. New flow when bridge detects service not bound tryAdbEnableA11y() runs a POSIX one-liner via AdbLocalClient.executeShellWithResult: reads settings get secure enabled_accessibility_services, appends our component com.byd.dashcast/com.byd.dashcast.ime.ClusterImeWatcherService if missing (preserves TalkBack & co — uses case ":$CUR:" in *":$COMP:"*) NEW="$CUR";; *) NEW="$CUR:$COMP";; esac), settings put secure enabled_accessibility_services "$NEW" && settings put secure accessibility_enabled 1 && echo OK:$NEW. On OK:… reply → short ✓ Toast + finish(). Next ⌨ tap routes typing end-to-end. On ADB failure (port 5555 closed, pairing dialog not accepted) → fallback to the v1.2.22 5-intent BYD-Accessibility launcher. Why this works SettingsProvider#assertWritePermissionsForSecureSettings admits any caller with WRITE_SECURE_SETTINGS. UID 2000 (shell) holds it system-wide, so AccessibilityManagerService rebinds our service on the next tick. Files changed app/build.gradle (213 → 214 / 1.2.22 → 1.2.23) KeyboardBridgeActivity.java (+~80 LoC: tryAdbEnableA11y() shell + 2-branch callback, promptAndOpenSettings() fallback helper) LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.22DL5 — Real fix for the a11y enable prompt Field log from v1.2.20 finally revealed the canonical android.settings.ACCESSIBILITY_SETTINGS action has no resolver on this BYD ROM: W KeyboardBridge: ACTION_ACCESSIBILITY_SETTINGS unavailable: No Activity found to handle Intent { act=android.settings.ACCESSIBILITY_SETTINGS flg=0x10000000 } Reverse-engineered settings_byd AndroidManifest reveals BYD ships its own action: android.intent.action.BYD_ACCESSIBILITY → com.byd.carsettings/com.byd.systemsettings.accessibility.AccessibilityMainActivity AccessibilityMainActivity calls AccessibilityManager.getInstalledAccessibilityServiceList() — same list where the user enables « DashCast Cluster IME ». Updated intent fallback chain (5 attempts) android.intent.action.BYD_ACCESSIBILITY (preferred on BYD) Explicit com.byd.carsettings/...AccessibilityMainActivity Canonical Settings.ACTION_ACCESSIBILITY_SETTINGS AOSP com.android.settings/...AccessibilitySettingsActivity Generic Settings.ACTION_SETTINGS Each gated by PackageManager.resolveActivity, each per-attempt logged. Files changed app/build.gradle (212 → 213 / 1.2.21 → 1.2.22) KeyboardBridgeActivity.java (+10 LoC, 2 new intents prepended) LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.21v1.2.21 — build 212 — DL5 KeyboardBridge: actually open Accessibility settings Symptom (v1.2.20 field feedback) The user saw the localized « DashCast Cluster IME » Toast but the Accessibility settings page never opened, leaving no way to enable the service from the prompt. Root cause v1.2.18 fired startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)) from inside onWindowFocusChanged with a single try/catch — on this BYD ROM the bare action either has no resolver or the AM silently drops the launch when called from a floating Dialog activity's focus callback. The catch branch never even fired (no ACTION_ACCESSIBILITY_SETTINGS unavailable line in the log). Fix Multi-attempt launcher. New openAccessibilitySettings() tries three intents in order: Settings.ACTION_ACCESSIBILITY_SETTINGS (canonical) explicit AOSP component com.android.settings/.Settings$AccessibilitySettingsActivity generic Settings.ACTION_SETTINGS as last resort Each attempt is gated by PackageManager.resolveActivity so we never start an unresolvable Intent; each branch logs attempt N unresolved/launched/failed. Deferred launch. Posted via mInput.post(…) instead of inline inside onWindowFocusChanged — some ROMs refuse startActivity from a focus callback. One-shot guard. New mPromptedA11y boolean prevents repeat Toasts / launch attempts as onWindowFocusChanged fires multiple times (IME pop/close). Behavioural contract DL5 testeur with a11y disabled: tap ⌨ → invisible bridge → localized Toast → next frame, Accessibility settings open automatically (one of the 3 intents will resolve). DL5 with a11y already enabled: prompt path skipped. DL3 / DL2: not exercised (bridge entry gated). Touched files app/build.gradle (211→212, 1.2.20→1.2.21) app/src/main/java/com/byd/dashcast/KeyboardBridgeActivity.java (+mPromptedA11y, +openAccessibilitySettings() ~30 LoC) CHANGELOG.md LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.20v1.2.20 — build 211 — i18n: 26 missing translations filled across 11 locales Pure i18n release. Translation parity restored. No logic change. No new permissions. No protocol bump. What changed Audit revealed that all 11 locale files (values-en/de/es/it/ru/uk/be/kk/uz/tr/ar) were each missing the same 29 keys vs the French base. 3 are translatable="false" (ota_progress_percent, ota_progress_unknown, ota_version_label) and rightfully excluded — leaving 26 strings × 11 locales = 286 entries that were silently falling back to French at runtime. Translated keys Keyboard bridge UI: keyboard_bridge_{title,hint,input_hint,btn_enter,btn_back,btn_close} (still used as the activity label in the manifest). ⌨ button + content description: btn_keyboard, btn_keyboard_desc. Diag tab labels: diag_tab_adas, diag_tab_dilink2, diag_tab_mirror. DL2 recon panel: diag_dl2_header_{title,subtitle,subtitle_fmt}, diag_dl2_pill_{unknown,detected,other}, diag_dl2_counters_fmt. Mirror diagnostic panel: diag_mirror_header_{title,subtitle,subtitle_fmt}, diag_mirror_pill_{unknown,dl5,other}, diag_mirror_hint, diag_mirror_send_log. Universal symbols / brand names (⌨, ✓ ✗ ! ⊘ counter format, ADAS, DiLink 2, Mirror) are carried as-is across all locales. Behavioural contract Runtime resource resolution now serves the user's locale for every translatable key — no more silent French fallback on Russian/English/German/Spanish/Italian/Ukrainian/Belarusian/Kazakh/Uzbek/Turkish/Arabic system locales. DL3 / DL5 / DL2: all benefit equally. Power-user surface exemption DiagActivity Toasts and SysInfoActivity build-info text remain FR-hardcoded — inside the power-user Sniffer/Diag surface that the project convention (tools:ignore="HardcodedText" on the layout root) intentionally exempts from i18n. Touched files app/build.gradle (210→211, 1.2.19→1.2.20) 11 × app/src/main/res/values-*/strings.xml (+26 lines each, +286 lines total) CHANGELOG.md LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.19v1.2.19 — build 210 — i18n: localized a11y prompt Toast Pure i18n release. No logic change. No new permissions. No protocol bump. What changed v1.2.18 introduced a hardcoded French Toast in KeyboardBridgeActivity.onWindowFocusChanged to nudge the user to enable the « DashCast Cluster IME » accessibility service when it is missing from Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES. That string is now extracted into a string resource and translated across all 11 existing locale folders. New string resource: keyboard_bridge_a11y_required_toast. Translated in: values (FR base), values-en, values-de, values-es, values-it, values-ru, values-uk, values-be, values-kk, values-uz, values-tr, values-ar. KeyboardBridgeActivity now reads it via getString(R.string.keyboard_bridge_a11y_required_toast). Behavioural contract DL5 testeur (non-French system locale): the a11y-prompt Toast now appears in the user's language. DL3 Seal EU: no impact (a11y prompt path is DL5-only). DL2: no impact (no cluster path). Touched files app/build.gradle (209→210, 1.2.18→1.2.19) app/src/main/java/com/byd/dashcast/KeyboardBridgeActivity.java (~3 LoC) 12 × app/src/main/res/values*/strings.xml (+1 line each) CHANGELOG.md LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.18DashCast v1.2.18 (build 209) — DL5: prompt user to enable a11y for keyboard + chain cmd activity task resize Field log: BYD_RE_Sniffer_20260523_161033.txt (post-v1.2.17 install). Confirmed both v1.2.17 surgical fixes landed (IME pops up cleanly, am task resize shell pipe runs) but two follow-on issues surfaced. Bug C — a11y service never bound (typed text doesn't reach Yandex) The IME now appears (✅ v1.2.17 worked) and the user types, but zero traces of ClusterImeWatcherService appear in the entire 20 MB log: $ grep -c ClusterImeWatcher BYD_RE_Sniffer_20260523_161033.txt 0 Meaning: the user hasn't enabled « DashCast Cluster IME » under Android Settings → Accessibility, so ClusterImeWatcherService.sInstance is null, setTextOnCluster() returns false silently, and the typed text never reaches Yandex. Android system policy: an app cannot self-grant accessibility — the user must opt in manually. Fix: in KeyboardBridgeActivity.onWindowFocusChanged, read Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, compare against our component name, and if absent: emit a clear WARN line in logcat, show a Toast in French explaining what to do, launch Settings.ACTION_ACCESSIBILITY_SETTINGS and finish() the bridge so the user lands directly on the right settings screen. Also added explicit AppLogger.w lines in setTextOnCluster() / performImeEnterOnCluster() for the null-instance and no-focused-editable branches (previously silent-returned false). Bug D — am task resize is a silent no-op on this BYD ROM Same field log: 30+ shell invocations succeed with empty output and no visible effect: D AdbLocalClient: executeShellWithResult: am task resize 26 348 244 1572 476 -> I ClusterService: resizeActiveTask (via AdbLocal `am task resize`) OK: On AOSP API 30+ the am verb was rewritten into cmd activity and am task resize is now a stub that prints nothing and does nothing. Fix: wrap the shell command with 2>&1; echo "exit=$?" to capture stderr + exit code; classify the result as looksOk = exit=0 && no "unknown command" / error / exception substrings. If not OK, chain a cmd activity task resize <id> <l> <t> <r> <b> attempt (the modern AOSP equivalent). Both attempts log their raw output for forward debugging. If neither works on the BYD ROM, the next iteration will route through BetaProxyClient when the daemon reconnects. Behavioural contract DL3 / DL2 — KeyboardBridgeActivity launch is gated by MainActivity.isDiLink5 upstream, so the new a11y prompt only fires on DL5. ClusterService resize is byte-for-byte identical on success (reflection wins on DL3); only the fallback chain is richer on DL5. No new permissions, no new dependencies, no protocol bump. Touched files app/build.gradle — 208 → 209. KeyboardBridgeActivity.java — a11y enablement check + Toast + Settings intent + isClusterImeWatcherEnabled helper (~55 LoC). ime/ClusterImeWatcherService.java — 3 WARN lines on silent-no-op paths. ClusterService.java — exit-code capture + cmd activity task resize chain (~40 LoC). Validation First tap on the keyboard icon with a11y still disabled → Toast + immediate Accessibility settings → user enables « DashCast Cluster IME » → next tap on keyboard icon → IME appears + typing routes to Yandex via ACTION_SET_TEXT. Logcat should show ClusterImeWatcher: onServiceConnected — watching cluster focus events. Move sliders + Apply → logcat shows EITHER: resizeActiveTask \am task resize` -> "...exit=0" (looksOk=true)` (resize works on the BYD ROM), OR looksOk=false followed by resizeActiveTask \cmd activity task resize` -> "..."` revealing which path the ROM accepts. APK DashCast-v1.2.18-debug.apk. LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.17DashCast v1.2.17 (build 208) — DL5: IME finally served + resize via am task resize (bypasses MANAGE_ACTIVITY_TASKS) Field log: BYD_RE_Sniffer_20260523_153544.txt (post-v1.2.16 install). Two clean diagnostics this time, each pinpointing the exact fix. Bug A — IME still doesn't appear (now width=0 instead of height=0) With v1.2.16's floating Dialog theme the EditText moved from 0,0-330,0 (height collapsed) → 0,0-0,37 (width collapsed): W InputMethodManager: Ignoring showSoftInput() as view=android.widget.EditText{... 0,0-0,37} is not served. Cause: a floating Dialog window auto-wraps its content. An empty EditText sized MATCH_PARENT × MATCH_PARENT wraps to width 0 because there's no text and no min size → IMM still rejects width-0 views as not-served. Fix: pass the EditText explicit pixel size via its own LayoutParams (not MATCH_PARENT) plus setMinWidth/setMinHeight belt-and-braces: mInput.setMinWidth(wPx); mInput.setMinHeight(hPx); setContentView(mInput, new ViewGroup.LayoutParams(wPx, hPx)); The Dialog wraps around 220×48 dp of concrete pixels → IMM sees a real bounding box → IME pops up. Bug B — SecurityException: requires android.permission.MANAGE_ACTIVITY_TASKS v1.2.16's exception unwrapping paid off — the real cause is now visible: W ClusterService: resizeActiveTask failed: SecurityException: Permission Denial: resizeTask() from pid=N, uid=10148 requires android.permission.MANAGE_ACTIVITY_TASKS That permission is signature|privileged — unreachable for a normal APK no matter what we declare in the manifest. Fix: when the reflection call throws, fall back to am task resize <taskId> <l> <t> <r> <b> via AdbLocalClient.executeShellWithResult — the exact same shell pipe that already executes wm overscan successfully. am runs in shell uid (2000) context, which has enough capabilities to bypass the app-level permission check. Async (callback-based), success/error logged. Behavioural contract DL3 — reflection succeeds on the legacy ROM, fallback never runs. Unchanged. DL5 — reflection now reliably throws SecurityException (logged once per attempt); shell fallback handles the actual resize. DL2 — cluster path inert; AdbLocalClient.executeShellWithResult already has a blockDiLink2Resize guard. No new permissions, no new dependencies, no protocol bump. Touched files app/build.gradle — 207 → 208. KeyboardBridgeActivity.java — explicit EditText pixel size + setMinWidth/setMinHeight (~5 LoC). ClusterService.java — shell fallback in resizeActiveTask after the reflection failure branch (~20 LoC). Validation Tap keyboard icon → invisible 220×48 dp window appears at bottom-right of head unit → IME pops up → typing routes through setTextOnCluster (a11y ACTION_SET_TEXT) to Yandex on the cluster. Move sliders + Apply → logcat shows: W ClusterService: resizeActiveTask reflection failed: SecurityException ... D AdbLocalClient: executeShellWithResult: am task resize 129 ... -> ... I ClusterService: resizeActiveTask (via AdbLocal `am task resize`) OK → visible resize on the cluster. If am task resize is missing on this BYD ROM (AOSP removed it API 30+; vendor may have kept it), the log will show the shell error verbatim and the next release will try the modern cmd activity syntax. APK DashCast-v1.2.17-debug.apk. LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.16DashCast v1.2.16 (build 207) — DL5: real KeyboardBridge IME fix + resize exception unwrapping Field log: BYD_RE_Sniffer_20260523_152515.txt (post-v1.2.15 install). Bug A — IME still doesn't appear (re-diagnosed) v1.2.15 changed SOFT_INPUT_ADJUST_RESIZE → SOFT_INPUT_ADJUST_NOTHING. Log shows the EditText is still 0,0-330,0 (height=0): W InputMethodManager: Ignoring showSoftInput() as view=android.widget.EditText{... 0,0-330,0} is not served. Real cause: @android:style/Theme.Translucent.NoTitleBar is translucent but not floating (windowIsFloating=false). For non-floating activities, Window.setLayout(wPx, hPx) is partially ignored — the width somehow ends up at 330 px (220 dp), but the height collapses to 0 (likely the IME insets the non-floating window from the bottom, and our window IS at the bottom). Fix: new dedicated KeyboardBridgeTheme extending Theme.DeviceDefault.Dialog with android:windowIsFloating=true, translucent, transparent background, no frame/title/dim/animations. Manifest switched to it. With a floating theme setLayout(220dp, 48dp) + Gravity.BOTTOM | END are properly honoured → EditText becomes 330×72 px in the corner → IMM serves it → IME pops up. Bug B — resizeActiveTask failed: null (partial success) Good news: Path 3 AdbLocal fallback in findRunningTaskId works: D ClusterService: findRunningTaskId ru.yandex.yandexmaps → taskId=109 (via AdbLocal dumpsys recents) Bad news: the actual reflection call failed 30+ times with an uninformative resizeActiveTask failed: null. Root cause of the null: we logged e.getMessage() on an InvocationTargetException wrapper — its own message IS null; the real cause is hidden behind getTargetException(). Fix: Unwrap InvocationTargetException → log class + message of the true cause. Try the 2-arg resizeTask(int, Rect) signature as a fallback when the 3-arg form throws NoSuchMethodException (vendor signature variance). Success log now includes cw / ch / clusterDisplay for forward debugging. Once the next field log surfaces the real exception (probably SecurityException requiring MANAGE_ACTIVITY_TASKS, or a vendor signature mismatch), the appropriate code path can be added. Behavioural contract DL3 / DL5 / DL2 — only the KeyboardBridge theme is new and only that Activity uses it. ClusterService.resizeActiveTask is byte-for-byte identical on success; the failure log is richer. No new permissions, no new dependencies, no protocol bump. Touched files app/build.gradle — 206 → 207, "1.2.15" → "1.2.16". res/values/styles.xml — +KeyboardBridgeTheme. AndroidManifest.xml — KeyboardBridgeActivity theme. ClusterService.java — ~40 LoC: dual-signature resizeTask + cause unwrapping + richer log. Validation Tap keyboard icon → IME pops up at bottom of head unit (small invisible 220×48 dp window in corner). Typed text reaches Yandex on cluster via a11y. Apply resize → logcat shows EITHER resizeActiveTask … OK (cw=… ch=… clusterDisplay=…) (success) OR the real exception name + message — no more null. APK DashCast-v1.2.16-debug.apk. LinksAPK download GitHub release page
-
DashCast — BYD Cluster Launcher & Mirror
DashCast v1.2.15DashCast v1.2.15 (build 206) — DL5: KeyboardBridge IME + resize fixes Field log: BYD_RE_Sniffer_20260523_150803.txt (post-v1.2.14 install, DL5 testeur). Bug A — IME never appears when tapping keyboard icon Logcat: W InputMethodManager: Ignoring showSoftInput() as view=android.widget.EditText{... 0,0-330,0} is not served. Geometry 0,0-330,0: width 330 px = 220 dp ≈ correct, but height 0. Root cause: v1.2.12 set SOFT_INPUT_ADJUST_RESIZE on a window pinned BOTTOM | END with height 48 dp. When the IME tries to show at the bottom, ADJUST_RESIZE shrinks the window to make room above the IME — but our window IS the bottom → no vertical room left → height collapses to 0 → IMM rejects the EditText as "not served" → IME never shows. Fix: replace SOFT_INPUT_ADJUST_RESIZE with SOFT_INPUT_ADJUST_NOTHING. Window keeps 220×48 dp regardless of IME. The IME visually overlaps the bridge but that's fine (transparent invisible chrome). Bug B — Resize still doesn't work (daemon not connected) Logcat repeating 40+ times during 12:00–12:05: W ClusterService: findRunningTaskId ru.yandex.yandexmaps — daemon not connected; cannot fallback to dumpsys W ClusterService: resizeActiveTask: taskId<=0 for pkg=ru.yandex.yandexmaps — cannot resize v1.2.13's daemon-dumpsys fallback is correct in principle but useless when the proxy daemon isn't running on the device. Same log proves AdbLocalClient.executeShellWithResult IS functional (D AdbLocalClient: executeShellWithResult: wm overscan ... lines present). Fix: add Path 3 in ClusterService.findRunningTaskId — if daemon disconnected, fall back to AdbLocalClient.executeShellWithResult("dumpsys activity recents") synchronously via CountDownLatch (5 s timeout). Reuses the existing parseTaskIdFromDumpsysRecents regex helper. Behavioural contract DL3 Seal EU — Path 1 (AM) succeeds, Paths 2/3 never reached. Unchanged. DL5 with daemon connected — Path 2 (daemon) succeeds, Path 3 never reached. Unchanged. DL5 with daemon off (this testeur) — Path 3 runs dumpsys over AdbLocal, parses Yandex's taskId, resizeTask applies. DL2 — no cluster path, both changes inert. Touched files app/build.gradle — versionCode 205→206, versionName 1.2.14→1.2.15. KeyboardBridgeActivity.java — SOFT_INPUT_ADJUST_RESIZE → SOFT_INPUT_ADJUST_NOTHING. ClusterService.java — Path 3 AdbLocal sync fallback in findRunningTaskId (~50 LoC). Validation Tap keyboard icon → IME pops up at bottom, no is not served warning. Typed text reaches Yandex on cluster via a11y. Apply resize → logcat: findRunningTaskId ru.yandex.yandexmaps → taskId=NN (via AdbLocal dumpsys recents) followed by resizeActiveTask ... OK → visible resize. If resizeTask still fails with SecurityException (MANAGE_ACTIVITY_TASKS required), the WARN resizeActiveTask failed: ... surfaces — next iteration would route through a typed daemon verb. APK DashCast-v1.2.15-debug.apk (≈14.5 MB). LinksAPK download GitHub release page