266 lines
10 KiB
Swift
266 lines
10 KiB
Swift
import UIKit
|
|
import Flutter
|
|
import FirebaseCore
|
|
import CarPlay
|
|
|
|
import CoreLocation
|
|
|
|
@main
|
|
@objc class AppDelegate: FlutterAppDelegate {
|
|
|
|
let locationManager = CLLocationManager()
|
|
|
|
override func application(
|
|
_ application: UIApplication,
|
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
|
) -> Bool {
|
|
|
|
|
|
|
|
// Now continue with the rest of the app initialization.
|
|
FirebaseApp.configure()
|
|
GeneratedPluginRegistrant.register(with: self)
|
|
|
|
// التأكد من أن الجهاز غير مخترق قبل تشغيل التطبيق
|
|
if SecurityChecks.isDeviceCompromised() {
|
|
showSecurityAlert()
|
|
return false
|
|
}
|
|
|
|
// تهيئة قنوات الاتصال مع Flutter
|
|
setupMethodChannel()
|
|
setupCarNavigationChannel()
|
|
|
|
// ضبط مدير الموقع لمتابعة تغييرات الأذونات
|
|
locationManager.delegate = self
|
|
locationManager.requestWhenInUseAuthorization()
|
|
|
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
|
}
|
|
|
|
// MARK: - CarPlay Connection (Non-Scene API)
|
|
|
|
override func application(
|
|
_ application: UIApplication,
|
|
didConnectCarInterfaceController interfaceController: CPInterfaceController,
|
|
to window: CPWindow
|
|
) {
|
|
let sceneDelegate = CarPlaySceneDelegate()
|
|
sceneDelegate.interfaceController = interfaceController
|
|
|
|
let mapTemplate = CPMapTemplate()
|
|
sceneDelegate.mapTemplate = mapTemplate
|
|
mapTemplate.guidanceBackgroundStyle = .light
|
|
|
|
interfaceController.setRootTemplate(mapTemplate, animated: true)
|
|
|
|
CarPlayState.shared.sceneDelegate = sceneDelegate
|
|
}
|
|
|
|
override func application(
|
|
_ application: UIApplication,
|
|
didDisconnectCarInterfaceController interfaceController: CPInterfaceController,
|
|
from window: CPWindow
|
|
) {
|
|
CarPlayState.shared.sceneDelegate?.stopNavigation()
|
|
CarPlayState.shared.sceneDelegate = nil
|
|
}
|
|
|
|
// MARK: - تهيئة القناة بين Swift و Flutter
|
|
func setupMethodChannel() {
|
|
guard let controller = window?.rootViewController as? FlutterViewController else {
|
|
return
|
|
}
|
|
|
|
let channel = FlutterMethodChannel(name: "com.siro.siro_driver/security",
|
|
binaryMessenger: controller.binaryMessenger)
|
|
channel.setMethodCallHandler { call, result in
|
|
switch call.method {
|
|
case "isNativeRooted":
|
|
result(SecurityChecks.isDeviceCompromised())
|
|
default:
|
|
result(FlutterMethodNotImplemented)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Car Navigation Method Channel
|
|
|
|
func setupCarNavigationChannel() {
|
|
guard let controller = window?.rootViewController as? FlutterViewController else {
|
|
return
|
|
}
|
|
|
|
let channel = FlutterMethodChannel(
|
|
name: "com.siro.siro_driver/car_navigation",
|
|
binaryMessenger: controller.binaryMessenger
|
|
)
|
|
channel.setMethodCallHandler { [weak self] call, result in
|
|
guard let self = self else { return }
|
|
switch call.method {
|
|
case "updateNavState":
|
|
let lat = (call.arguments as? [String: Any])?["lat"] as? Double ?? 0
|
|
let lng = (call.arguments as? [String: Any])?["lng"] as? Double ?? 0
|
|
let bearing = (call.arguments as? [String: Any])?["bearing"] as? Double ?? 0
|
|
let speed = (call.arguments as? [String: Any])?["speed"] as? Double ?? 0
|
|
let instruction = (call.arguments as? [String: Any])?["instruction"] as? String ?? ""
|
|
let distanceToStep = (call.arguments as? [String: Any])?["distanceToStep"] as? Double ?? 0
|
|
let eta = (call.arguments as? [String: Any])?["eta"] as? Double ?? 0
|
|
let maneuver = (call.arguments as? [String: Any])?["maneuver"] as? Int ?? 0
|
|
let isNavigating = (call.arguments as? [String: Any])?["isNavigating"] as? Bool ?? false
|
|
|
|
DispatchQueue.main.async {
|
|
guard let delegate = CarPlayState.shared.sceneDelegate else { return }
|
|
if isNavigating {
|
|
if delegate.navigationSession == nil {
|
|
delegate.startNavigation(
|
|
instruction: instruction,
|
|
distance: distanceToStep,
|
|
eta: eta,
|
|
maneuverType: maneuver
|
|
)
|
|
} else {
|
|
delegate.updateManeuver(
|
|
instruction: instruction,
|
|
distance: distanceToStep,
|
|
maneuverType: maneuver
|
|
)
|
|
delegate.updateArrivalEstimates(eta: eta)
|
|
}
|
|
delegate.updateLocation(lat: lat, lng: lng, bearing: bearing)
|
|
} else {
|
|
delegate.stopNavigation()
|
|
}
|
|
}
|
|
result(true)
|
|
|
|
case "updateLocation":
|
|
let lat = (call.arguments as? [String: Any])?["lat"] as? Double ?? 0
|
|
let lng = (call.arguments as? [String: Any])?["lng"] as? Double ?? 0
|
|
let bearing = (call.arguments as? [String: Any])?["bearing"] as? Double ?? 0
|
|
|
|
DispatchQueue.main.async {
|
|
CarPlayState.shared.sceneDelegate?.updateLocation(
|
|
lat: lat,
|
|
lng: lng,
|
|
bearing: bearing
|
|
)
|
|
}
|
|
result(true)
|
|
|
|
case "updateInstruction":
|
|
let instruction = (call.arguments as? [String: Any])?["instruction"] as? String ?? ""
|
|
let maneuver = (call.arguments as? [String: Any])?["maneuver"] as? Int ?? 0
|
|
let distanceToStep = (call.arguments as? [String: Any])?["distanceToStep"] as? Double ?? 0
|
|
|
|
DispatchQueue.main.async {
|
|
CarPlayState.shared.sceneDelegate?.updateManeuver(
|
|
instruction: instruction,
|
|
distance: distanceToStep,
|
|
maneuverType: maneuver
|
|
)
|
|
}
|
|
result(true)
|
|
|
|
case "stopNavigation":
|
|
DispatchQueue.main.async {
|
|
CarPlayState.shared.sceneDelegate?.stopNavigation()
|
|
}
|
|
result(true)
|
|
|
|
default:
|
|
result(FlutterMethodNotImplemented)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - عرض تنبيه عند اكتشاف جهاز مخترق
|
|
func showSecurityAlert() {
|
|
guard let rootVC = UIApplication.shared.keyWindow?.rootViewController else {
|
|
exit(0)
|
|
}
|
|
|
|
let alert = UIAlertController(
|
|
title: "Security Warning".localized,
|
|
message: "Compromised device detected. Exiting.".localized,
|
|
preferredStyle: .alert
|
|
)
|
|
|
|
alert.addAction(UIAlertAction(title: "OK".localized, style: .destructive) { _ in
|
|
self.obfuscatedExit()
|
|
})
|
|
|
|
if #available(iOS 15.0, *) {
|
|
alert.popoverPresentationController?.sourceView = rootVC.view
|
|
alert.popoverPresentationController?.sourceRect = CGRect(x: rootVC.view.bounds.midX, y: rootVC.view.bounds.midY, width: 0, height: 0)
|
|
alert.popoverPresentationController?.permittedArrowDirections = []
|
|
alert.presentationController?.delegate = self
|
|
|
|
if let presentationController = alert.presentationController as? UISheetPresentationController {
|
|
presentationController.detents = [.medium()]
|
|
presentationController.preferredCornerRadius = 20
|
|
presentationController.prefersEdgeAttachedInCompactHeight = true
|
|
presentationController.prefersGrabberVisible = false
|
|
}
|
|
}
|
|
|
|
rootVC.present(alert, animated: true, completion: nil)
|
|
}
|
|
|
|
// MARK: - إخفاء عملية الخروج بطريقة مشفرة
|
|
func obfuscatedExit() {
|
|
let selector = NSSelectorFromString(String(format: "%@%@", "ex", "it:"))
|
|
if responds(to: selector) {
|
|
perform(selector, with: 0)
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// MARK: - التعامل مع أذونات الموقع بطريقة آمنة
|
|
extension AppDelegate: CLLocationManagerDelegate {
|
|
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
|
|
if #available(iOS 14.0, *) {
|
|
let status = manager.authorizationStatus
|
|
switch status {
|
|
case .authorizedWhenInUse, .authorizedAlways:
|
|
print("Authorization granted")
|
|
case .denied, .restricted:
|
|
print("Authorization denied")
|
|
case .notDetermined:
|
|
manager.requestWhenInUseAuthorization()
|
|
@unknown default:
|
|
break
|
|
}
|
|
} else {
|
|
// لنظام iOS 13 وما قبله، نستخدم الطريقة القديمة
|
|
let status = CLLocationManager.authorizationStatus()
|
|
switch status {
|
|
case .authorizedWhenInUse, .authorizedAlways:
|
|
print("Authorization granted")
|
|
case .denied, .restricted:
|
|
print("Authorization denied")
|
|
case .notDetermined:
|
|
manager.requestWhenInUseAuthorization()
|
|
@unknown default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - منع إغلاق التنبيهات الأمنية
|
|
extension AppDelegate: UIAdaptivePresentationControllerDelegate {
|
|
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// MARK: - دعم الترجمة للنصوص
|
|
extension String {
|
|
var localized: String {
|
|
return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
|
|
}
|
|
}
|