Files
element-x-ios/ElementX/Sources/Application/Application.swift
T
Stefan Ceriu 2777075655 Handle the non-deprecated INStartCallIntent for calls from Recents
The call-from-Recents handling was wired entirely to the deprecated
INStartVideoCallIntent (the activity-type registration and the cast), which
also meant the app never handled the modern INStartCallIntent that superseded
it in iOS 13. Switch both to INStartCallIntent and derive isVoiceCall from its
callCapability. Clears the deprecation warning.
2026-06-29 18:10:30 +03:00

150 lines
5.9 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Copyright 2025 Element Creations Ltd.
// Copyright 2022-2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import SwiftUI
@main
struct Application: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
@Environment(\.openURL) private var openURL
@Environment(\.openWindow) private var openWindow
@Environment(\.dismissWindow) private var dismissWindow
@Environment(\.displayScale) private var displayScale
private var appCoordinator: AppCoordinatorProtocol!
init() {
if ProcessInfo.isRunningUITests {
appCoordinator = UITestsAppCoordinator(appDelegate: appDelegate)
} else if ProcessInfo.isRunningUnitTests {
appCoordinator = UnitTestsAppCoordinator(appDelegate: appDelegate)
} else if ProcessInfo.isRunningAccessibilityTests {
appCoordinator = AccessibilityTestsAppCoordinator(appDelegate: appDelegate)
} else {
appCoordinator = AppCoordinator(appDelegate: appDelegate)
}
SceneDelegate.windowManager = appCoordinator.windowManager
}
var body: some Scene {
WindowGroup(id: SceneDelegate.mainSceneID) {
appCoordinator.toPresentable()
.statusBarHidden(shouldHideStatusBar)
.overlay(alignment: .top) {
if #available(iOS 26, *), ProcessInfo.processInfo.isiOSAppOnMac {
// Fake an old-school titlebar to reduce the "floaty-ness" of everything with liquid glass.
Divider().ignoresSafeArea()
}
}
.environment(\.openURL, openURLAction(appCoordinator: appCoordinator, windowType: nil))
.onOpenURL { url in
openURL(url, isExternalURL: true)
}
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in
// Universal Links opened from the Camera app's QR scanner (and NFC tags) are
// delivered as a browsing-web user activity rather than through `onOpenURL` 🤷‍♂️
guard let url = userActivity.webpageURL else { return }
openURL(url, isExternalURL: true)
}
.onContinueUserActivity("INStartCallIntent") { userActivity in
appCoordinator.handleUserActivity(userActivity)
}
.task {
UserAgentBuilder.displayScale = displayScale
appCoordinator.start()
appCoordinator.windowManager.configure(withOpenWindowAction: openWindow,
dismissWindowAction: dismissWindow)
}
}
.handlesExternalEvents(matching: ["*"])
.commands {
CommandGroup(replacing: .newItem) {
Button(L10n.actionStartChat) { }
.disabled(true)
}
CommandGroup(replacing: .appSettings) {
Button(L10n.commonSettings) {
appCoordinator.handleAppRoute(.settings, windowType: nil)
}
.keyboardShortcut(",", modifiers: .command)
}
CommandGroup(after: .windowArrangement) {
Button("Global Search") {
appCoordinator.handleAppRoute(.search, windowType: nil)
}
.keyboardShortcut("k", modifiers: [.command])
}
}
// This is invoked in response of the WindowManager receiving a register
// coordinator request and invoking the `OpenWindowAction` with which
// it's configured in the task above.
WindowGroup(for: SecondaryWindowType.self) { $type in
if let type {
appCoordinator.windowManager.windowForType(type)
.environment(\.openURL, openURLAction(appCoordinator: appCoordinator, windowType: type))
}
}
.defaultSize(width: ProcessInfo.processInfo.isiOSAppOnMac ? 600 : 400, height: 800)
.windowResizability(.contentSize)
}
private func openURLAction(appCoordinator: AppCoordinatorProtocol, windowType: SecondaryWindowType?) -> OpenURLAction {
.init { url in
if appCoordinator.handleDeepLink(url, isExternalURL: false, windowType: windowType) {
return .handled
}
if appCoordinator.handlePotentialPhishingAttempt(url: url, openURLAction: { url in
openURL(url, isExternalURL: false)
}) {
return .handled
}
return .systemAction
}
}
// MARK: - Private
private func openURL(_ url: URL, isExternalURL: Bool) {
if !appCoordinator.handleDeepLink(url, isExternalURL: isExternalURL, windowType: nil) {
openURLInSystemBrowser(url)
}
}
/// Hide the status bar so it doesn't interfere with the screenshot tests
private var shouldHideStatusBar: Bool {
ProcessInfo.isRunningUITests
}
/// https://github.com/element-hq/element-x-ios/issues/1824
/// Avoid opening universal links in other app variants and infinite loops between them
private func openURLInSystemBrowser(_ originalURL: URL) {
guard var urlComponents = URLComponents(url: originalURL, resolvingAgainstBaseURL: true) else {
openURL(originalURL)
return
}
var queryItems = urlComponents.queryItems ?? []
queryItems.append(.init(name: "no_universal_links", value: "true"))
urlComponents.queryItems = queryItems
guard let url = urlComponents.url else {
openURL(originalURL)
return
}
openURL(url)
}
}