9ef0196d52
- fix(keyboard): drop unusable extensionContext.open; report the real result of UIApplication.open and cancel the start watchdog immediately on failure with clear "open OSGKeyboard" guidance instead of a 30s spin - feat(capture): recover from audio route changes (AirPods/wired headset plug-unplug) and phone-call interruptions by rebuilding the engine/tap against the live hardware format - feat(home): add a "Start" button in the Home footer when a session is inactive and permissions are granted (no-jump manual restart) - feat(hostapps): expand the return whitelist to 46 apps (Signal, Zoom, Instagram, X, Douyin, Zhihu, Bilibili, Things, Firefox, ...) with en/zh-Hans names and synced LSApplicationQueriesSchemes - feat(island): use a "ready" checkmark instead of a mic glyph while idle and set a staleDate so an orphaned Live Activity fades after a force-quit - feat(coldstart): use the template OSG brand mark instead of a system waveform glyph in the cold-start overlay - perf(keyboard): rely on Darwin notifications as the primary session signal and drop the fallback poll from 1s to 3s - fix(keyboard): remove the redundant "Start" text button next to the inactive-session hint on the keyboard - docs: correct the per-take cap to 210s (3.5 min) and document that force-quitting no longer resurrects the session (tracker + READMEs)
40 lines
1.5 KiB
Swift
40 lines
1.5 KiB
Swift
// HostAppLauncher.swift
|
||
// OSGKeyboard · Keyboard Extension
|
||
//
|
||
// Opens the host app from the keyboard extension.
|
||
//
|
||
// Reality check (verified against iOS 18–26 behaviour):
|
||
// • `extensionContext.open` is documented for Today widgets only; for a
|
||
// keyboard extension it resolves `false`, so we do not use it.
|
||
// • The deprecated `openURL:` selector hack was disabled in iOS 18
|
||
// ("BUG IN CLIENT OF UIKIT … migrate to open(_:options:completionHandler:)").
|
||
// • The still-working path is: walk the responder chain to `UIApplication`
|
||
// and call the non-deprecated `open(_:options:completionHandler:)`. This
|
||
// requires Full Access and grows less reliable on newer iOS, so we report
|
||
// the *real* success from the completion handler instead of assuming it
|
||
// worked — callers degrade to on-keyboard guidance when it returns false.
|
||
|
||
import UIKit
|
||
|
||
enum HostAppLauncher {
|
||
@MainActor
|
||
static func open(
|
||
url: URL,
|
||
from controller: KeyboardViewController,
|
||
completion: @escaping @MainActor (Bool) -> Void
|
||
) {
|
||
var responder: UIResponder? = controller
|
||
while let current = responder {
|
||
if let application = current as? UIApplication {
|
||
application.open(url, options: [:]) { success in
|
||
Task { @MainActor in completion(success) }
|
||
}
|
||
return
|
||
}
|
||
responder = current.next
|
||
}
|
||
// No `UIApplication` in the responder chain — cannot open the host app.
|
||
completion(false)
|
||
}
|
||
}
|