authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-03 18:53:21-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-03 18:53:21-07:00
log5989de44a24888dab02854477ea1d0f50ac3a4a6
tree225b7bfe83ab52b2d67f8b1a2b4986a3e361df60
parentc5ba51d629b3813182cf3e161cc3f23b1e541dc3
signature Signed by PGP key B5690EEEBB952194

fix(mobile): keep store screenshots free of system banners and show dictation (#9548)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

2 files changed, 46 insertions(+), 1 deletions(-)

apps/mobile/src/features/voice-input/useVoiceInputController.ts+4-1
......@@ -14,6 +14,7 @@ import { useSharedValue } from "react-native-reanimated";
1414
1515import type { ComposerEditorSelection } from "../../components/ComposerEditor";
1616import { getLocalVoiceTranscriber } from "../../native/voiceTranscription";
17import { getNativeShowcaseScene } from "../showcase/nativeShowcaseScene";
1718import {
1819 VoiceInputController,
1920 VOICE_RECORDING_LIMIT_SECONDS,
......@@ -203,7 +204,9 @@ export function useVoiceInputController(input: {
203204 const cancel = useCallback(() => controller.cancel(), [controller]);
204205
205206 return {
206 isAvailable: getLocalVoiceTranscriber() !== null,
207 // Store screenshots show the dictation button even on simulators, whose
208 // on-device transcription is unavailable.
209 isAvailable: getLocalVoiceTranscriber() !== null || getNativeShowcaseScene() !== null,
207210 state,
208211 audioLevels,
209212 elapsedSeconds,
scripts/mobile-showcase.ts+42
......@@ -806,6 +806,47 @@ async function ensureIosSimulator(device: ShowcaseIosDevice): Promise<{
806806 };
807807}
808808
809async function iosSimulatorDataPath(udid: string): Promise<string> {
810 const parsed = JSON.parse(await commandOutput("xcrun", ["simctl", "list", "devices", "-j"])) as {
811 readonly devices: Readonly<
812 Record<string, ReadonlyArray<SimctlDevice & { readonly dataPath: string }>>
813 >;
814 };
815 const dataPath = Object.values(parsed.devices)
816 .flat()
817 .find((device) => device.udid === udid)?.dataPath;
818 if (!dataPath) throw new Error(`Could not resolve the data path of iOS simulator ${udid}.`);
819 return dataPath;
820}
821
822// generativeexperiencesd posts a "Ready for Apple Intelligence" follow-up
823// banner on an eligible device's first boot, and CoreFollowUp re-surfaces it
824// on every boot until the user dismisses it. Stamping the readiness marker
825// before boot makes the daemon skip the post, and dropping the CoreFollowUp
826// store clears a banner that a previous boot already queued. Runs while the
827// device is shut down so the files are read fresh on the next boot.
828async function suppressIosSystemFollowUps(udid: string): Promise<void> {
829 const dataPath = await iosSimulatorDataPath(udid);
830 const preferences = NodePath.join(dataPath, "Library/Preferences");
831 await NodeFSP.mkdir(preferences, { recursive: true });
832 await NodeFSP.writeFile(
833 NodePath.join(preferences, "com.apple.generativeexperiences.corefollowup.plist"),
834 `<?xml version="1.0" encoding="UTF-8"?>
835<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
836<plist version="1.0">
837<dict>
838\t<key>DateOfLastAppleIntelligenceReadinessCFU</key>
839\t<date>2020-01-01T00:00:00Z</date>
840</dict>
841</plist>
842`,
843 );
844 await NodeFSP.rm(NodePath.join(dataPath, "Library/CoreFollowUp"), {
845 recursive: true,
846 force: true,
847 });
848}
849
809850async function normalizeIosSimulator(appearance: ShowcaseAppearance, udid: string): Promise<void> {
810851 await runCommand("xcrun", ["simctl", "ui", udid, "appearance", appearance]);
811852 await runCommand("xcrun", [
......@@ -922,6 +963,7 @@ async function captureIos(
922963 // confirmations, keyboards) without erasing the developer's simulator.
923964 await runCommand("xcrun", ["simctl", "shutdown", simulator.udid]);
924965 }
966 await suppressIosSystemFollowUps(simulator.udid);
925967 await runCommand("xcrun", ["simctl", "boot", simulator.udid]);
926968 await runCommand("xcrun", ["simctl", "bootstatus", simulator.udid, "-b"]);
927969 if (capture.device.orientation === "landscape") {