LocationViewController.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2012-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. #import "LocationViewController.h"
  21. #import "LocationMessage.h"
  22. #import "Conversation.h"
  23. #import "Contact.h"
  24. #import "MyIdentityStore.h"
  25. #import "UIImage+ColoredImage.h"
  26. #ifdef DEBUG
  27. static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
  28. #else
  29. static const DDLogLevel ddLogLevel = DDLogLevelWarning;
  30. #endif
  31. @interface LocationViewController () <CLLocationManagerDelegate>
  32. @property CLLocationManager *locationManager;
  33. @end
  34. @implementation LocationViewController {
  35. NSInteger showInMapsButtonIndex;
  36. NSInteger calculateRouteButtonIndex;
  37. BOOL annotationSelected;
  38. }
  39. @synthesize locationMessage;
  40. - (void)dealloc {
  41. self.mapView.delegate = nil;
  42. }
  43. - (void)viewDidLoad {
  44. [super viewDidLoad];
  45. self.mapView.userTrackingMode = MKUserTrackingModeNone;
  46. self.mapView.showsUserLocation = YES;
  47. MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(self.coordinate, 1000, 1000);
  48. [self.mapView setRegion:region animated:NO];
  49. [self setupColors];
  50. }
  51. - (void)setupColors {
  52. [self.view setBackgroundColor:[Colors backgroundLight]];
  53. [self.toolbar setTintColor:[Colors main]];
  54. [self.toolbar setBarTintColor:[Colors background]];
  55. }
  56. - (void)viewWillAppear:(BOOL)animated {
  57. [super viewWillAppear:animated];
  58. [self checkPermissions];
  59. [self.mapView removeAnnotation:self];
  60. [self.mapView addAnnotation:self];
  61. [self.mapView removeOverlays:self.mapView.overlays];
  62. DDLogVerbose(@"accuracy: %f", self.locationMessage.accuracy.doubleValue);
  63. if (self.locationMessage.accuracy != nil && self.locationMessage.accuracy.doubleValue > 5.0) {
  64. MKCircle *accuracyCircle = [MKCircle circleWithCenterCoordinate:self.coordinate radius:self.locationMessage.accuracy.doubleValue];
  65. [self.mapView addOverlay:accuracyCircle];
  66. }
  67. MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(self.coordinate, 1000, 1000);
  68. [self.mapView setRegion:region animated:NO];
  69. }
  70. - (void)viewDidDisappear:(BOOL)animated {
  71. /* must remove annotation or we will cause retain cycle */
  72. [self.mapView removeAnnotation:self];
  73. [super viewDidDisappear:animated];
  74. }
  75. - (BOOL)shouldAutorotate {
  76. return YES;
  77. }
  78. -(UIInterfaceOrientationMask)supportedInterfaceOrientations {
  79. if (SYSTEM_IS_IPAD) {
  80. return UIInterfaceOrientationMaskAll;
  81. }
  82. return UIInterfaceOrientationMaskAllButUpsideDown;
  83. }
  84. - (CLLocationCoordinate2D)coordinate {
  85. return CLLocationCoordinate2DMake(self.locationMessage.latitude.doubleValue, self.locationMessage.longitude.doubleValue);
  86. }
  87. - (IBAction)actionButton:(id)sender {
  88. UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
  89. [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"show_in_maps", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
  90. [self showInMaps];
  91. }]];
  92. [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"calculate_route", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
  93. [self calculateRoute];
  94. }]];
  95. [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"cancel", nil) style:UIAlertActionStyleCancel handler:nil]];
  96. [self presentViewController:actionSheet animated:YES completion:nil];
  97. }
  98. - (IBAction)mapTypeChanged:(id)sender {
  99. switch (self.mapTypeControl.selectedSegmentIndex) {
  100. case 0:
  101. self.mapView.mapType = MKMapTypeStandard;
  102. break;
  103. case 1:
  104. self.mapView.mapType = MKMapTypeHybrid;
  105. break;
  106. case 2:
  107. self.mapView.mapType = MKMapTypeSatellite;
  108. break;
  109. }
  110. }
  111. - (IBAction)gotoUserLocationButtonTapped:(id)sender {
  112. MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(self.mapView.userLocation.location.coordinate, 1000, 1000);
  113. [self.mapView setRegion:region animated:YES];
  114. }
  115. - (IBAction)gotoLocationButtonTapped:(id)sender {
  116. MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(self.coordinate, 1000, 1000);
  117. [self.mapView setRegion:region animated:YES];
  118. }
  119. - (NSString *)title {
  120. if (self.locationMessage.isOwn.boolValue) {
  121. if ([MyIdentityStore sharedMyIdentityStore].pushFromName != nil)
  122. return [MyIdentityStore sharedMyIdentityStore].pushFromName;
  123. else
  124. return [MyIdentityStore sharedMyIdentityStore].identity;
  125. } else {
  126. if (self.locationMessage.sender != nil)
  127. return self.locationMessage.sender.displayName;
  128. else
  129. return self.locationMessage.conversation.contact.displayName;
  130. }
  131. }
  132. - (NSString *)subtitle {
  133. return [DateFormatter mediumStyleDateTime:self.locationMessage.remoteSentDate];
  134. }
  135. - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
  136. if (annotation == self) {
  137. if (@available(iOS 11.0, *)) {
  138. static NSString *markerAnnotationIdentifier = @"MarkerAnnotation";
  139. MKMarkerAnnotationView *markerAnnotation = (MKMarkerAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:markerAnnotationIdentifier];
  140. if (markerAnnotation == nil) {
  141. markerAnnotation = [[MKMarkerAnnotationView alloc] initWithAnnotation:self reuseIdentifier:markerAnnotationIdentifier];
  142. markerAnnotation.canShowCallout = true;
  143. markerAnnotation.markerTintColor = [Colors main];
  144. markerAnnotation.animatesWhenAdded = true;
  145. }
  146. return markerAnnotation;
  147. } else {
  148. static NSString *pinAnnotationIdentifier = @"PinAnnotation";
  149. MKPinAnnotationView *pinAnnotation = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:pinAnnotationIdentifier];
  150. if (pinAnnotation == nil) {
  151. pinAnnotation = [[MKPinAnnotationView alloc] initWithAnnotation:self reuseIdentifier:pinAnnotationIdentifier];
  152. pinAnnotation.canShowCallout = YES;
  153. pinAnnotation.pinTintColor = [Colors main];
  154. pinAnnotation.animatesDrop = true;
  155. }
  156. return pinAnnotation;
  157. }
  158. }
  159. return nil;
  160. }
  161. - (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay {
  162. if ([overlay isKindOfClass:[MKCircle class]]) {
  163. MKCircleRenderer *circelRenderer = [[MKCircleRenderer alloc] initWithCircle:overlay];
  164. circelRenderer.fillColor = [UIColor colorWithRed:0.48 green:0.86 blue:0.11 alpha:0.3];
  165. circelRenderer.strokeColor = [UIColor colorWithRed:0.48 green:0.86 blue:0.11 alpha:0.7];
  166. circelRenderer.lineWidth = 2.0 * [UIScreen mainScreen].scale;
  167. return circelRenderer;
  168. }
  169. return nil;
  170. }
  171. - (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView {
  172. if (!annotationSelected) {
  173. [mapView selectAnnotation:self animated:YES];
  174. annotationSelected = YES;
  175. }
  176. }
  177. - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
  178. /* do nothing */
  179. }
  180. - (void)showInMaps {
  181. Class itemClass = [MKMapItem class];
  182. if (itemClass && [itemClass respondsToSelector:@selector(openMapsWithItems:launchOptions:)]) {
  183. MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithCoordinate:self.coordinate addressDictionary:nil]];
  184. mapItem.name = self.title;
  185. [mapItem openInMapsWithLaunchOptions:nil];
  186. } else {
  187. /* IOS 5 */
  188. NSString *mapsUrl = [NSString stringWithFormat:@"http://maps.apple.com/maps?ll=%f,%f", self.coordinate.latitude, self.coordinate.longitude];
  189. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:mapsUrl] options:@{} completionHandler:nil];
  190. }
  191. }
  192. - (void)calculateRoute {
  193. Class itemClass = [MKMapItem class];
  194. if (itemClass && [itemClass respondsToSelector:@selector(openMapsWithItems:launchOptions:)]) {
  195. MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithCoordinate:self.coordinate addressDictionary:nil]];
  196. mapItem.name = self.title;
  197. [mapItem openInMapsWithLaunchOptions:[NSDictionary dictionaryWithObject:MKLaunchOptionsDirectionsModeDriving forKey:MKLaunchOptionsDirectionsModeKey]];
  198. } else {
  199. /* IOS 5 */
  200. CLLocation *myLocation = self.mapView.userLocation.location;
  201. NSString *mapsUrl = [NSString stringWithFormat:@"http://maps.apple.com/maps?saddr=%f,%f&daddr=%f,%f",
  202. myLocation.coordinate.latitude, myLocation.coordinate.longitude, self.coordinate.latitude, self.coordinate.longitude];
  203. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:mapsUrl] options:@{} completionHandler:nil];
  204. }
  205. }
  206. - (void)checkPermissions {
  207. _locationManager = [[CLLocationManager alloc] init];
  208. [_locationManager requestWhenInUseAuthorization];
  209. _locationManager.delegate = self;
  210. }
  211. #pragma mark - CLLocationManagerDelegate
  212. -(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
  213. if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse) {
  214. self.mapView.showsUserLocation = YES;
  215. } else {
  216. self.mapView.showsUserLocation = NO;
  217. }
  218. }
  219. @end