2022-04-21 20:04:22 +01:00
|
|
|
//
|
|
|
|
// AppDelegate.swift
|
|
|
|
// SimpleX
|
|
|
|
//
|
|
|
|
// Created by Evgeny on 30/03/2022.
|
|
|
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
import UIKit
|
2022-05-31 07:55:13 +01:00
|
|
|
import SimpleXChat
|
2024-07-03 22:42:13 +01:00
|
|
|
import SwiftUI
|
2022-04-21 20:04:22 +01:00
|
|
|
|
|
|
|
class AppDelegate: NSObject, UIApplicationDelegate {
|
|
|
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
|
|
|
logger.debug("AppDelegate: didFinishLaunchingWithOptions")
|
|
|
|
application.registerForRemoteNotifications()
|
2024-02-27 04:24:07 +07:00
|
|
|
removePasscodesIfReinstalled()
|
2024-07-03 22:42:13 +01:00
|
|
|
prepareForLaunch()
|
2024-11-30 23:29:27 +07:00
|
|
|
deleteOldChatArchive()
|
2022-04-21 20:04:22 +01:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
|
|
|
let token = deviceToken.map { String(format: "%02hhx", $0) }.joined()
|
|
|
|
logger.debug("AppDelegate: didRegisterForRemoteNotificationsWithDeviceToken \(token)")
|
2022-04-23 06:32:16 +01:00
|
|
|
let m = ChatModel.shared
|
2022-07-01 09:49:30 +01:00
|
|
|
let deviceToken = DeviceToken(pushProvider: PushProvider(env: pushEnvironment), token: token)
|
2022-06-27 23:03:27 +01:00
|
|
|
m.deviceToken = deviceToken
|
2023-12-11 12:59:49 +00:00
|
|
|
// savedToken is set in startChat, when it is started before this method is called
|
2022-07-01 09:49:30 +01:00
|
|
|
if m.savedToken != nil {
|
|
|
|
registerToken(token: deviceToken)
|
2022-05-30 16:15:17 +04:00
|
|
|
}
|
2022-04-21 20:04:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
|
|
|
|
logger.error("AppDelegate: didFailToRegisterForRemoteNotificationsWithError \(error.localizedDescription)")
|
|
|
|
}
|
|
|
|
|
|
|
|
func application(_ application: UIApplication,
|
|
|
|
didReceiveRemoteNotification userInfo: [AnyHashable : Any],
|
|
|
|
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
|
|
|
logger.debug("AppDelegate: didReceiveRemoteNotification")
|
2022-07-01 09:49:30 +01:00
|
|
|
let m = ChatModel.shared
|
2022-04-22 13:46:05 +01:00
|
|
|
if let ntfData = userInfo["notificationData"] as? [AnyHashable : Any],
|
2022-07-01 09:49:30 +01:00
|
|
|
m.notificationMode != .off {
|
2022-04-22 13:46:05 +01:00
|
|
|
if let verification = ntfData["verification"] as? String,
|
|
|
|
let nonce = ntfData["nonce"] as? String {
|
2023-07-19 16:37:46 +01:00
|
|
|
if let token = m.deviceToken {
|
2022-04-22 13:46:05 +01:00
|
|
|
logger.debug("AppDelegate: didReceiveRemoteNotification: verification, confirming \(verification)")
|
|
|
|
Task {
|
|
|
|
do {
|
2022-04-23 06:32:16 +01:00
|
|
|
if case .active = m.tokenStatus {} else { m.tokenStatus = .confirmed }
|
2022-06-27 23:03:27 +01:00
|
|
|
try await apiVerifyToken(token: token, nonce: nonce, code: verification)
|
2022-04-23 06:32:16 +01:00
|
|
|
m.tokenStatus = .active
|
2022-04-22 13:46:05 +01:00
|
|
|
} catch {
|
2025-05-05 11:51:22 +01:00
|
|
|
if let cr = error as? ChatError, case .errorAgent(.NTF(.AUTH)) = cr {
|
2022-04-23 06:32:16 +01:00
|
|
|
m.tokenStatus = .expired
|
|
|
|
}
|
2022-04-22 13:46:05 +01:00
|
|
|
logger.error("AppDelegate: didReceiveRemoteNotification: apiVerifyToken or apiIntervalNofication error: \(responseError(error))")
|
|
|
|
}
|
|
|
|
completionHandler(.newData)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
completionHandler(.noData)
|
|
|
|
}
|
2022-04-21 20:04:22 +01:00
|
|
|
} else if let checkMessages = ntfData["checkMessages"] as? Bool, checkMessages {
|
|
|
|
logger.debug("AppDelegate: didReceiveRemoteNotification: checkMessages")
|
2023-12-18 10:36:25 +00:00
|
|
|
if m.ntfEnablePeriodic && allowBackgroundRefresh() && BGManager.shared.lastRanLongAgo {
|
2022-07-02 08:50:25 +01:00
|
|
|
receiveMessages(completionHandler)
|
|
|
|
} else {
|
|
|
|
completionHandler(.noData)
|
|
|
|
}
|
2022-04-22 13:46:05 +01:00
|
|
|
} else {
|
|
|
|
completionHandler(.noData)
|
2022-04-21 20:04:22 +01:00
|
|
|
}
|
2022-04-22 13:46:05 +01:00
|
|
|
} else {
|
|
|
|
completionHandler(.noData)
|
2022-04-21 20:04:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-06 14:07:27 +01:00
|
|
|
func applicationWillTerminate(_ application: UIApplication) {
|
2023-07-20 12:07:00 +01:00
|
|
|
logger.debug("DEBUGGING: AppDelegate: applicationWillTerminate")
|
2023-01-27 22:09:39 +00:00
|
|
|
ChatModel.shared.filesToDelete.forEach {
|
|
|
|
removeFile($0)
|
|
|
|
}
|
|
|
|
ChatModel.shared.filesToDelete = []
|
2022-07-06 14:07:27 +01:00
|
|
|
terminateChat()
|
|
|
|
}
|
|
|
|
|
2022-08-20 21:55:06 +01:00
|
|
|
func application(_ application: UIApplication,
|
|
|
|
configurationForConnecting connectingSceneSession: UISceneSession,
|
|
|
|
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
|
|
|
let configuration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
|
|
|
|
if connectingSceneSession.role == .windowApplication {
|
|
|
|
configuration.delegateClass = SceneDelegate.self
|
|
|
|
}
|
|
|
|
return configuration
|
|
|
|
}
|
|
|
|
|
2022-04-21 20:04:22 +01:00
|
|
|
private func receiveMessages(_ completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
|
|
|
let complete = BGManager.shared.completionHandler {
|
|
|
|
logger.debug("AppDelegate: completed BGManager.receiveMessages")
|
|
|
|
completionHandler(.newData)
|
|
|
|
}
|
|
|
|
|
|
|
|
BGManager.shared.receiveMessages(complete)
|
|
|
|
}
|
2023-11-08 00:56:38 +08:00
|
|
|
|
2024-02-27 04:24:07 +07:00
|
|
|
private func removePasscodesIfReinstalled() {
|
|
|
|
// Check for the database existence, because app and self destruct passcodes
|
|
|
|
// will be saved and restored by iOS when a user deletes and re-installs the app.
|
|
|
|
// In this case the database and settings will be deleted, but the passcodes won't be.
|
|
|
|
// Deleting passcodes ensures that the user will not get stuck on "Opening app..." screen.
|
|
|
|
if (kcAppPassword.get() != nil || kcSelfDestructPassword.get() != nil) &&
|
|
|
|
!UserDefaults.standard.bool(forKey: DEFAULT_PERFORM_LA) && !hasDatabase() {
|
|
|
|
_ = kcAppPassword.remove()
|
|
|
|
_ = kcSelfDestructPassword.remove()
|
|
|
|
_ = kcDatabasePassword.remove()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-03 22:42:13 +01:00
|
|
|
private func prepareForLaunch() {
|
|
|
|
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
|
|
|
|
}
|
|
|
|
|
2023-11-08 00:56:38 +08:00
|
|
|
static func keepScreenOn(_ on: Bool) {
|
|
|
|
UIApplication.shared.isIdleTimerDisabled = on
|
|
|
|
}
|
2022-04-21 20:04:22 +01:00
|
|
|
}
|
2022-08-20 21:55:06 +01:00
|
|
|
|
|
|
|
class SceneDelegate: NSObject, ObservableObject, UIWindowSceneDelegate {
|
|
|
|
var window: UIWindow?
|
2024-07-03 22:42:13 +01:00
|
|
|
static var windowStatic: UIWindow?
|
2022-11-11 12:30:10 +04:00
|
|
|
var windowScene: UIWindowScene?
|
2022-08-20 21:55:06 +01:00
|
|
|
|
|
|
|
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
2024-07-03 22:42:13 +01:00
|
|
|
UITableView.appearance().backgroundColor = .clear
|
2022-08-20 21:55:06 +01:00
|
|
|
guard let windowScene = scene as? UIWindowScene else { return }
|
2022-11-11 12:30:10 +04:00
|
|
|
self.windowScene = windowScene
|
2022-08-20 21:55:06 +01:00
|
|
|
window = windowScene.keyWindow
|
2024-07-03 22:42:13 +01:00
|
|
|
SceneDelegate.windowStatic = windowScene.keyWindow
|
|
|
|
migrateAccentColorAndTheme()
|
|
|
|
ThemeManager.applyTheme(currentThemeDefault.get())
|
|
|
|
ThemeManager.adjustWindowStyle()
|
|
|
|
}
|
|
|
|
|
|
|
|
private func migrateAccentColorAndTheme() {
|
|
|
|
let defs = UserDefaults.standard
|
|
|
|
/// For checking migration
|
|
|
|
// themeOverridesDefault.set([])
|
|
|
|
// currentThemeDefault.set(DefaultTheme.SYSTEM_THEME_NAME)
|
|
|
|
// defs.set(0.5, forKey: DEFAULT_ACCENT_COLOR_RED)
|
|
|
|
// defs.set(0.3, forKey: DEFAULT_ACCENT_COLOR_GREEN)
|
|
|
|
// defs.set(0.8, forKey: DEFAULT_ACCENT_COLOR_BLUE)
|
|
|
|
|
|
|
|
let userInterfaceStyle = getUserInterfaceStyleDefault()
|
|
|
|
if defs.double(forKey: DEFAULT_ACCENT_COLOR_GREEN) == 0 && userInterfaceStyle == .unspecified {
|
|
|
|
// No migration needed or already migrated
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
let defaultAccentColor = Color(cgColor: CGColor(red: 0.000, green: 0.533, blue: 1.000, alpha: 1))
|
|
|
|
let accentColor = Color(cgColor: getUIAccentColorDefault())
|
|
|
|
if accentColor != defaultAccentColor {
|
|
|
|
let colors = ThemeColors(primary: accentColor.toReadableHex())
|
|
|
|
var overrides = themeOverridesDefault.get()
|
|
|
|
var themeIds = currentThemeIdsDefault.get()
|
|
|
|
switch userInterfaceStyle {
|
|
|
|
case .light:
|
|
|
|
let light = ThemeOverrides(base: DefaultTheme.LIGHT, colors: colors, wallpaper: ThemeWallpaper(preset: PresetWallpaper.school.filename))
|
|
|
|
overrides.append(light)
|
|
|
|
themeOverridesDefault.set(overrides)
|
|
|
|
themeIds[DefaultTheme.LIGHT.themeName] = light.themeId
|
|
|
|
currentThemeIdsDefault.set(themeIds)
|
|
|
|
ThemeManager.applyTheme(DefaultTheme.LIGHT.themeName)
|
|
|
|
case .dark:
|
|
|
|
let dark = ThemeOverrides(base: DefaultTheme.DARK, colors: colors, wallpaper: ThemeWallpaper(preset: PresetWallpaper.school.filename))
|
|
|
|
overrides.append(dark)
|
|
|
|
themeOverridesDefault.set(overrides)
|
|
|
|
themeIds[DefaultTheme.DARK.themeName] = dark.themeId
|
|
|
|
currentThemeIdsDefault.set(themeIds)
|
|
|
|
ThemeManager.applyTheme(DefaultTheme.DARK.themeName)
|
|
|
|
case .unspecified:
|
|
|
|
let light = ThemeOverrides(base: DefaultTheme.LIGHT, colors: colors, wallpaper: ThemeWallpaper(preset: PresetWallpaper.school.filename))
|
|
|
|
let dark = ThemeOverrides(base: DefaultTheme.DARK, colors: colors, wallpaper: ThemeWallpaper(preset: PresetWallpaper.school.filename))
|
|
|
|
overrides.append(light)
|
|
|
|
overrides.append(dark)
|
|
|
|
themeOverridesDefault.set(overrides)
|
|
|
|
themeIds[DefaultTheme.LIGHT.themeName] = light.themeId
|
|
|
|
themeIds[DefaultTheme.DARK.themeName] = dark.themeId
|
|
|
|
currentThemeIdsDefault.set(themeIds)
|
|
|
|
ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME)
|
|
|
|
@unknown default: ()
|
|
|
|
}
|
|
|
|
} else if userInterfaceStyle != .unspecified {
|
|
|
|
let themeName = switch userInterfaceStyle {
|
|
|
|
case .light: DefaultTheme.LIGHT.themeName
|
|
|
|
case .dark: DefaultTheme.DARK.themeName
|
|
|
|
default: DefaultTheme.SYSTEM_THEME_NAME
|
|
|
|
}
|
|
|
|
ThemeManager.applyTheme(themeName)
|
|
|
|
}
|
|
|
|
defs.removeObject(forKey: DEFAULT_ACCENT_COLOR_RED)
|
|
|
|
defs.removeObject(forKey: DEFAULT_ACCENT_COLOR_GREEN)
|
|
|
|
defs.removeObject(forKey: DEFAULT_ACCENT_COLOR_BLUE)
|
|
|
|
defs.removeObject(forKey: DEFAULT_USER_INTERFACE_STYLE)
|
2022-08-20 21:55:06 +01:00
|
|
|
}
|
|
|
|
}
|