Merge commit '7621e2f8dec938cf48181c8b10afc9b01f444e68' into beta

This commit is contained in:
Ilya Laktyushin
2025-12-06 02:17:48 +04:00
commit 8344b97e03
28070 changed files with 7995182 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "DeviceLocationManager",
module_name = "DeviceLocationManager",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
],
visibility = [
"//visibility:public",
],
)
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>
@@ -0,0 +1,19 @@
//
// DeviceLocationManager.h
// DeviceLocationManager
//
// Created by Peter on 8/4/19.
// Copyright © 2019 Telegram Messenger LLP. All rights reserved.
//
#import <UIKit/UIKit.h>
//! Project version number for DeviceLocationManager.
FOUNDATION_EXPORT double DeviceLocationManagerVersionNumber;
//! Project version string for DeviceLocationManager.
FOUNDATION_EXPORT const unsigned char DeviceLocationManagerVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <DeviceLocationManager/PublicHeader.h>
@@ -0,0 +1,185 @@
import Foundation
import CoreLocation
import SwiftSignalKit
public enum DeviceLocationMode: Int32 {
case preciseForeground = 0
case preciseAlways = 1
}
private final class DeviceLocationSubscriber {
let id: Int32
let mode: DeviceLocationMode
let update: (CLLocation, Double?) -> Void
init(id: Int32, mode: DeviceLocationMode, update: @escaping (CLLocation, Double?) -> Void) {
self.id = id
self.mode = mode
self.update = update
}
}
private func getTopMode(subscribers: [DeviceLocationSubscriber]) -> DeviceLocationMode? {
var mode: DeviceLocationMode?
for subscriber in subscribers {
if mode == nil || subscriber.mode.rawValue > mode!.rawValue {
mode = subscriber.mode
}
}
return mode
}
public final class DeviceLocationManager: NSObject {
private let queue: Queue
private let log: ((String) -> Void)?
private let manager: CLLocationManager
private var requestedAuthorization = false
private var nextSubscriberId: Int32 = 0
private var subscribers: [DeviceLocationSubscriber] = []
private var currentTopMode: DeviceLocationMode?
private var currentLocation: CLLocation?
private var currentHeading: CLHeading?
public init(queue: Queue, log: ((String) -> Void)? = nil) {
assert(queue.isCurrent())
self.queue = queue
self.log = log
self.manager = CLLocationManager()
super.init()
self.manager.delegate = self
self.manager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
self.manager.distanceFilter = kCLDistanceFilterNone
self.manager.activityType = .other
self.manager.pausesLocationUpdatesAutomatically = false
self.manager.headingFilter = 2.0
if #available(iOS 11.0, *) {
self.manager.showsBackgroundLocationIndicator = true
}
}
public func push(mode: DeviceLocationMode, updated: @escaping (CLLocation, Double?) -> Void) -> Disposable {
assert(self.queue.isCurrent())
let id = self.nextSubscriberId
self.nextSubscriberId += 1
self.subscribers.append(DeviceLocationSubscriber(id: id, mode: mode, update: updated))
if let currentLocation = self.currentLocation {
updated(currentLocation, self.currentHeading?.magneticHeading)
}
self.updateTopMode()
let queue = self.queue
return ActionDisposable { [weak queue, weak self] in
if let queue = queue {
queue.async {
if let strongSelf = self {
loop: for i in 0 ..< strongSelf.subscribers.count {
if strongSelf.subscribers[i].id == id {
strongSelf.subscribers.remove(at: i)
break loop
}
}
strongSelf.updateTopMode()
}
}
}
}
}
private func updateTopMode() {
assert(self.queue.isCurrent())
let previousTopMode = self.currentTopMode
let topMode = getTopMode(subscribers: self.subscribers)
if topMode != previousTopMode {
self.currentTopMode = topMode
if let topMode = topMode {
self.log?("setting mode \(topMode)")
if previousTopMode == nil {
if !self.requestedAuthorization {
self.requestedAuthorization = true
self.manager.requestAlwaysAuthorization()
}
switch topMode {
case .preciseForeground:
self.manager.allowsBackgroundLocationUpdates = false
case .preciseAlways:
self.manager.allowsBackgroundLocationUpdates = true
}
self.manager.startUpdatingLocation()
self.manager.startUpdatingHeading()
}
} else {
self.currentLocation = nil
self.manager.stopUpdatingLocation()
self.log?("stopped")
}
}
}
}
extension CLHeading {
var effectiveHeading: Double? {
if self.headingAccuracy < 0.0 {
return nil
}
if self.trueHeading > 0.0 {
return self.trueHeading
} else {
return self.magneticHeading
}
}
}
extension DeviceLocationManager: CLLocationManagerDelegate {
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
assert(self.queue.isCurrent())
if let location = locations.first {
if self.currentTopMode != nil {
self.currentLocation = location
for subscriber in self.subscribers {
subscriber.update(location, self.currentHeading?.effectiveHeading)
}
}
}
}
public func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
assert(self.queue.isCurrent())
if self.currentTopMode != nil {
self.currentHeading = newHeading
if let currentLocation = self.currentLocation {
for subscriber in self.subscribers {
subscriber.update(currentLocation, newHeading.effectiveHeading)
}
}
}
}
}
public func currentLocationManagerCoordinate(manager: DeviceLocationManager, timeout timeoutValue: Double) -> Signal<CLLocationCoordinate2D?, NoError> {
return (
Signal { subscriber in
let disposable = manager.push(mode: .preciseForeground, updated: { location, _ in
subscriber.putNext(location.coordinate)
subscriber.putCompletion()
})
return disposable
}
|> runOn(Queue.mainQueue())
)
|> timeout(timeoutValue, queue: Queue.mainQueue(), alternate: .single(nil))
}