also looking at this
fix(native): interaction, audio, document and checkpoint gaps stranded after #401
#415
19 files
+987
-50
| @@ -1391,6 +1391,40 @@ unchanged. `craft.files.download` resolves a non-2xx response as | ||
| 1391 | 1391 | `{ ok: false, status, body }`; transport failures still reject with the native |
| 1392 | 1392 | network error codes. |
| 1393 | 1393 | |
| 1394 | Both maintained native hosts implement `craft.files.pickFile`. Android maps the | |
| 1395 | shared MIME, UTI, and common extension filters to `ACTION_OPEN_DOCUMENT`, copies | |
| 1396 | each selected document into the same app-owned directory, and enforces the | |
| 1397 | shared 512 MiB default/per-file limit before publishing a descriptor. A | |
| 1398 | dismissed picker resolves with an empty array; a canceled or oversized copy | |
| 1399 | never leaves a partial file behind. The same cancellation cleanup applies while | |
| 1400 | Android is decoding a picked image or copying a video, so a request canceled | |
| 1401 | after the picker closes cannot publish or leak an app-owned file. Use this for | |
| 1402 | GPX, TCX, FIT, PDF, or other documents that are not photos or videos. On iOS, | |
| 1403 | failed photo JPEG creation or finalization also removes its staging directory, | |
| 1404 | so a rejected image import does not accumulate orphaned temporary files. | |
| 1405 | ||
| 1406 | `craft.audio` playback is screen-owned on both hosts. Android requests audio | |
| 1407 | focus before preparing a player, reports a denied request as `AUDIO_FOCUS_DENIED`, | |
| 1408 | pauses on a transient interruption and resumes on focus gain, ducks during a | |
| 1409 | duckable interruption, and releases focus when the last sound ends or the | |
| 1410 | screen closes. iOS observes `AVAudioSession` interruptions and resumes sounds | |
| 1411 | only when the system grants the `shouldResume` option. A permanent Android | |
| 1412 | focus loss releases every player and sends each sound's error event; replacement, | |
| 1413 | decode failure, cancellation, interruption, and teardown also release their | |
| 1414 | player resources. | |
| 1415 | Control calls resolve `false` when their sound id is not owned by the screen, | |
| 1416 | matching the browser host instead of reporting a false success. | |
| 1417 | ||
| 1418 | Checkpoint names use the same bounded filename contract on both native hosts: | |
| 1419 | letters, digits, `.`, `_`, and `-`, up to 128 characters, without a leading | |
| 1420 | dot. Non-JSON values and invalid names are rejected before a write is posted; | |
| 1421 | the previous confirmed checkpoint remains available after a failed or | |
| 1422 | interrupted replacement. If a screen closes while a checkpoint is still being | |
| 1423 | written, its promise rejects instead of hanging; the host's later worker | |
| 1424 | completion cannot publish a second result for that request. iOS also reports a | |
| 1425 | failed result when its writer completes after the screen runtime is disposed or | |
| 1426 | has already been deallocated. | |
| 1427 | ||
| 1394 | 1428 | The `media` capability (iOS) keeps video and audio on the phone for offline, |
| 1395 | 1429 | in Application Support rather than the temporary directory, left out of |
| 1396 | 1430 | iCloud backups. `craft.media.keep(url, { headers })` downloads the file once |