also looking at this
fix(ios): settle a Siri shortcut removal whose completion never comes
#227
7 files
+503
-94
| @@ -64,10 +64,19 @@ const char craft_slice_page[] = | ||
| 64 | 64 | " if (sentSpeechEnd || !e.detail || typeof e.detail !== 'object') { return; }\n" |
| 65 | 65 | " sentSpeechEnd = true;\n" |
| 66 | 66 | " bridge.postMessage({t:'mobile', a:'getDeviceInfo', i:72});\n" |
| 67 | " // Genuinely last: this backgrounds the app, and every chain that needs\n" | |
| 68 | " // a foreground process has now closed.\n" | |
| 69 | " bridge.postMessage({t:'mobile', a:'openSettings', i:102});\n" | |
| 67 | " speechEnded = true;\n" | |
| 68 | " openSettingsWhenDone();\n" | |
| 70 | 69 | "});\n" |
| 70 | "// Genuinely last: opening Settings backgrounds the app, and a suspended\n" | |
| 71 | "// app runs neither a framework completion nor a main-queue deadline. So\n" | |
| 72 | "// this waits for the Siri removal to settle first, by its completion or\n" | |
| 73 | "// by its 15 s deadline (#211). Before, the removal was often still waiting\n" | |
| 74 | "// when the app was backgrounded here, and a suspended app never heard it.\n" | |
| 75 | "function openSettingsWhenDone() {\n" | |
| 76 | " if (postedSettings || !speechEnded || !siriSettled) { return; }\n" | |
| 77 | " postedSettings = true;\n" | |
| 78 | " bridge.postMessage({t:'mobile', a:'openSettings', i:102});\n" | |
| 79 | "}\n" | |
| 71 | 80 | "var sentEvent = false;\n" |
| 72 | 81 | "var sentRoundTrip = false, sentHandOff = false, sentTierZero = false, sentSecure = false, sentDb = false, sentNotif = false;\n" |
| 73 | 82 | "var bridge = window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.craft;\n" |
| @@ -84,6 +93,8 @@ const char craft_slice_page[] = | ||
| 84 | 93 | "var sentShot = false, sentDeepLink = false;\n" |
| 85 | 94 | "var sentOcr = false, sentClassify = false, sentDetect = false;\n" |
| 86 | 95 | "var sentAuth = false, sentSiriAdd = false, sentSiriDel = false;\n" |
| 96 | "var sentSiriTimeout = false, sentSiriOdd = false, siriSettled = false;\n" | |
| 97 | "var speechEnded = false, postedSettings = false;\n" | |
| 87 | 98 | "var sentPdfOpen = false, sentPdfClose = false;\n" |
| 88 | 99 | "var sentDownload = false, sentWatch = false, sentSettings = false;\n" |
| 89 | 100 | "var sentBtStart = false, sentBtStop = false;\n" |
| @@ -91,6 +102,19 @@ const char craft_slice_page[] = | ||
| 91 | 102 | "var sentLocationFresh = false, sentLocationCached = false, sentLocationTimeout = false;\n" |
| 92 | 103 | "var sentSpeechStart = false, sentSpeechErr = false, sentSpeechEnd = false;\n" |
| 93 | 104 | "window.__craftBridgeError = function (ctx) {\n" |
| 105 | " // A registration that failed never posts the removal, and a removal\n" | |
| 106 | " // that was refused or timed out still settles it, so neither can hold\n" | |
| 107 | " // openSettings back for ever.\n" | |
| 108 | " if (ctx && ctx.id === 96) { siriSettled = true; openSettingsWhenDone(); }\n" | |
| 109 | " if (ctx && ctx.id === 97) {\n" | |
| 110 | " siriSettled = true;\n" | |
| 111 | " if (ctx.code === 'TIMEOUT') {\n" | |
| 112 | " if (!sentSiriTimeout) { sentSiriTimeout = true; bridge.postMessage({t:'mobile', a:'getDeviceInfo', i:84}); }\n" | |
| 113 | " } else if (!sentSiriOdd) {\n" | |
| 114 | " sentSiriOdd = true; bridge.postMessage({t:'mobile', a:'getDeviceInfo', i:86});\n" | |
| 115 | " }\n" | |
| 116 | " openSettingsWhenDone();\n" | |
| 117 | " }\n" | |
| 94 | 118 | " // The message crossed Zig's escaping with a backslash, quote, and\n" |
| 95 | 119 | " // newline intact; anything mangled and this equality fails, i=7 never\n" |
| 96 | 120 | " // fires, and the harness reports the error route broken.\n" |
| @@ -318,11 +342,16 @@ const char craft_slice_page[] = | ||
| 318 | 342 | " // removeSiriShortcut answers from inside Foundation's completion\n" |
| 319 | 343 | " // handler, a void(^)(void) that carries nothing at all — so this reply\n" |
| 320 | 344 | " // arriving proves the block fired and its parked payload was delivered\n" |
| 321 | " // under the request id captured back at dispatch.\n" | |
| 322 | " if (!sentSiriDel && action === 'removeSiriShortcut' && payload\n" | |
| 323 | " && payload.removed === true && payload.action === 'sliceAct') {\n" | |
| 324 | " sentSiriDel = true;\n" | |
| 325 | " bridge.postMessage({t:'mobile', a:'getDeviceInfo', i:38});\n" | |
| 345 | " // under the request id captured back at dispatch. Anything else it\n" | |
| 346 | " // resolves with is odd (i=86); its deadline arrives as an error (i=84).\n" | |
| 347 | " if (action === 'removeSiriShortcut' && id === 97) {\n" | |
| 348 | " siriSettled = true;\n" | |
| 349 | " if (payload && payload.removed === true && payload.action === 'sliceAct') {\n" | |
| 350 | " if (!sentSiriDel) { sentSiriDel = true; bridge.postMessage({t:'mobile', a:'getDeviceInfo', i:38}); }\n" | |
| 351 | " } else if (!sentSiriOdd) {\n" | |
| 352 | " sentSiriOdd = true; bridge.postMessage({t:'mobile', a:'getDeviceInfo', i:86});\n" | |
| 353 | " }\n" | |
| 354 | " openSettingsWhenDone();\n" | |
| 326 | 355 | " }\n" |
| 327 | 356 | " if (!sentOcr && action === 'recognizeText' && Array.isArray(payload)) {\n" |
| 328 | 357 | " var joined = payload.map(function (r) { return r.text; }).join(' ').toUpperCase();\n" |