feat(flow): add Picture in Picture keep-alive mode

- Add FlowKeepAliveMode (Dynamic Island vs PiP) with iCloud sync
- Settings: keep-alive picker; inactivity timeout only for Live Activity
- PiP: waveform sample-buffer controller, mic released between utterances
- PiP sessions have no idle expiry; user closing PiP ends the session
- FlowSessionManager branches hostReady, session start, and utterance paths
- UIBackgroundModes picture-in-picture; bilingual strings and tests

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-07-26 10:34:52 +00:00
parent e6f99d2744
commit 70ba3a4359
18 changed files with 801 additions and 36 deletions
@@ -33,6 +33,7 @@ final class AppGroupConfigurationTests: XCTestCase {
XCTAssertEqual(config.polishIntensity, .default)
XCTAssertTrue(config.personalDictionary.entries.isEmpty)
XCTAssertTrue(config.flowSkipAppSwitch)
XCTAssertEqual(config.flowKeepAliveMode, .liveActivity)
XCTAssertEqual(config.flowInactivityDuration, .fiveMinutes)
}
@@ -58,6 +59,7 @@ final class AppGroupConfigurationTests: XCTestCase {
config.cursorDragNavigationEnabled = false
config.polishIntensity = .light
config.flowSkipAppSwitch = false
config.flowKeepAliveMode = .pictureInPicture
// Use a non-default value so the round-trip actually proves persistence.
config.flowInactivityDuration = .threeHours
config.save(to: defaults)
@@ -81,6 +83,7 @@ final class AppGroupConfigurationTests: XCTestCase {
XCTAssertFalse(loaded.cursorDragNavigationEnabled)
XCTAssertEqual(loaded.polishIntensity, .light)
XCTAssertFalse(loaded.flowSkipAppSwitch)
XCTAssertEqual(loaded.flowKeepAliveMode, .pictureInPicture)
XCTAssertEqual(loaded.flowInactivityDuration, .threeHours)
}