current_geo_location_mac.mm 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "platform/mac/current_geo_location_mac.h"
  8. #include "base/platform/mac/base_utilities_mac.h"
  9. #include "core/current_geo_location.h"
  10. #include <CoreLocation/CoreLocation.h>
  11. @interface LocationDelegate : NSObject<CLLocationManagerDelegate>
  12. - (id) initWithCallback:(Fn<void(Core::GeoLocation)>)callback;
  13. - (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations;
  14. - (void) locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error;
  15. - (void) locationManager:(CLLocationManager *) manager didChangeAuthorizationStatus:(CLAuthorizationStatus) status;
  16. - (void) dealloc;
  17. @end
  18. @implementation LocationDelegate {
  19. CLLocationManager *_manager;
  20. Fn<void(Core::GeoLocation)> _callback;
  21. }
  22. - (void) fail {
  23. [_manager stopUpdatingLocation];
  24. const auto onstack = _callback;
  25. [self release];
  26. onstack({});
  27. }
  28. - (void) processWithStatus:(CLAuthorizationStatus)status {
  29. switch (status) {
  30. case kCLAuthorizationStatusNotDetermined:
  31. if (@available(macOS 10.15, *)) {
  32. [_manager requestWhenInUseAuthorization];
  33. } else {
  34. [_manager startUpdatingLocation];
  35. }
  36. break;
  37. case kCLAuthorizationStatusAuthorizedAlways:
  38. [_manager startUpdatingLocation];
  39. return;
  40. case kCLAuthorizationStatusRestricted:
  41. case kCLAuthorizationStatusDenied:
  42. default:
  43. [self fail];
  44. return;
  45. }
  46. }
  47. - (id) initWithCallback:(Fn<void(Core::GeoLocation)>)callback {
  48. if (self = [super init]) {
  49. _callback = std::move(callback);
  50. _manager = [[CLLocationManager alloc] init];
  51. _manager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
  52. _manager.delegate = self;
  53. if ([CLLocationManager locationServicesEnabled]) {
  54. if (@available(macOS 11, *)) {
  55. [self processWithStatus:[_manager authorizationStatus]];
  56. } else {
  57. [self processWithStatus:[CLLocationManager authorizationStatus]];
  58. }
  59. } else {
  60. [self fail];
  61. }
  62. }
  63. return self;
  64. }
  65. - (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation*>*)locations {
  66. [_manager stopUpdatingLocation];
  67. auto result = Core::GeoLocation();
  68. if ([locations count] > 0) {
  69. const auto coordinate = [locations lastObject].coordinate;
  70. result.accuracy = Core::GeoLocationAccuracy::Exact;
  71. result.point = QPointF(coordinate.latitude, coordinate.longitude);
  72. }
  73. const auto onstack = _callback;
  74. [self release];
  75. onstack(result);
  76. }
  77. - (void) locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
  78. if (error.code != kCLErrorLocationUnknown) {
  79. [self fail];
  80. }
  81. }
  82. - (void) locationManager:(CLLocationManager *) manager didChangeAuthorizationStatus:(CLAuthorizationStatus) status {
  83. [self processWithStatus:status];
  84. }
  85. - (void) dealloc {
  86. if (_manager) {
  87. _manager.delegate = nil;
  88. [_manager release];
  89. }
  90. [super dealloc];
  91. }
  92. @end
  93. namespace Platform {
  94. void ResolveCurrentExactLocation(Fn<void(Core::GeoLocation)> callback) {
  95. [[LocationDelegate alloc] initWithCallback:std::move(callback)];
  96. }
  97. void ResolveLocationAddress(
  98. const Core::GeoLocation &location,
  99. const QString &language,
  100. Fn<void(Core::GeoAddress)> callback) {
  101. CLGeocoder *geocoder = [[CLGeocoder alloc] init];
  102. CLLocation *request = [[CLLocation alloc]
  103. initWithLatitude:location.point.x()
  104. longitude:location.point.y()];
  105. [geocoder reverseGeocodeLocation:request completionHandler:^(
  106. NSArray<CLPlacemark*> * __nullable placemarks,
  107. NSError * __nullable error) {
  108. if (placemarks && [placemarks count] > 0) {
  109. CLPlacemark *placemark = [placemarks firstObject];
  110. auto list = QStringList();
  111. const auto push = [&](NSString *text) {
  112. if (text) {
  113. const auto qt = NS2QString(text);
  114. if (!qt.isEmpty()) {
  115. list.push_back(qt);
  116. }
  117. }
  118. };
  119. push([placemark thoroughfare]);
  120. push([placemark locality]);
  121. push([placemark country]);
  122. callback({ .name = list.join(u", "_q) });
  123. } else {
  124. callback({});
  125. }
  126. [geocoder release];
  127. }];
  128. [request release];
  129. }
  130. } // namespace Platform