<commit_before>#import "MWMLocationManager.h"
#import <Pushwoosh/PushNotificationManager.h>
#import "MWMAlertViewController.h"
#import "MWMCommon.h"
#import "MWMController.h"
#import "MWMLocationObserver.h"
#import "MWMLocationPredictor.h"
#import "MapsAppDelegate.h"
#import "Statistics.h"

#import "3party/Alohalytics/src/alohalytics_objc.h"

#include "Framework.h"

#include "map/gps_tracker.hpp"

#include "std/map.hpp"

namespace
{
using TObserver = id<MWMLocationObserver>;
using TObservers = NSHashTable<__kindof TObserver>;

location::GpsInfo gpsInfoFromLocation(CLLocation * l, location::TLocationSource source)
{
  location::GpsInfo info;
  info.m_source = source;

  info.m_latitude = l.coordinate.latitude;
  info.m_longitude = l.coordinate.longitude;
  info.m_timestamp = l.timestamp.timeIntervalSince1970;

  if (l.horizontalAccuracy >= 0.0)
    info.m_horizontalAccuracy = l.horizontalAccuracy;

  if (l.verticalAccuracy >= 0.0)
  {
    info.m_verticalAccuracy = l.verticalAccuracy;
    info.m_altitude = l.altitude;
  }

  if (l.course >= 0.0)
    info.m_bearing = l.course;

  if (l.speed >= 0.0)
    info.m_speed = l.speed;
  return info;
}

location::CompassInfo compassInfoFromHeading(CLHeading * h)
{
  location::CompassInfo info;
  if (h.trueHeading >= 0.0)
    info.m_bearing = my::DegToRad(h.trueHeading);
  else if (h.headingAccuracy >= 0.0)
    info.m_bearing = my::DegToRad(h.magneticHeading);
  return info;
}

typedef NS_OPTIONS(NSUInteger, MWMLocationFrameworkUpdate) {
  MWMLocationFrameworkUpdateNone = 0,
  MWMLocationFrameworkUpdateLocation = 1 << 0,
  MWMLocationFrameworkUpdateHeading = 1 << 1,
  MWMLocationFrameworkUpdateStatus = 1 << 2
};

enum class GeoMode
{
  Pending,
  InPosition,
  NotInPosition,
  FollowAndRotate,
  VehicleRouting,
  PedestrianRouting,
  BicycleRouting
};

struct DesiredAccuracy
{
  CLLocationAccuracy charging;
  CLLocationAccuracy battery;
};

struct GeoModeSettings
{
  CLLocationDistance distanceFilter;
  DesiredAccuracy accuracy;
};

map<GeoMode, GeoModeSettings> const kGeoSettings{
    {GeoMode::Pending,
     {.distanceFilter = kCLDistanceFilterNone,
      .accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
                   .battery = kCLLocationAccuracyBestForNavigation}}},
    {GeoMode::InPosition,
     {.distanceFilter = 2,
      .accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
                   .battery = kCLLocationAccuracyBest}}},
    {GeoMode::NotInPosition,
     {.distanceFilter = 5,
      .accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
                   .battery = kCLLocationAccuracyBest}}},
    {GeoMode::FollowAndRotate,
     {.distanceFilter = 2,
      .accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
                   .battery = kCLLocationAccuracyBest}}},
    {GeoMode::VehicleRouting,
     {.distanceFilter = kCLDistanceFilterNone,
      .accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
                   .battery = kCLLocationAccuracyBest}}},
    {GeoMode::PedestrianRouting,
     {.distanceFilter = 2,
      .accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
                   .battery = kCLLocationAccuracyBest}}},
    {GeoMode::BicycleRouting,
     {.distanceFilter = 2,
      .accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
                   .battery = kCLLocationAccuracyBest}}}};

BOOL keepRunningInBackground()
{
  bool const needGPSForTrackRecorder = GpsTracker::Instance().IsEnabled();
  if (needGPSForTrackRecorder)
    return YES;

  auto const & f = GetFramework();
  bool const isRouteBuilt = f.IsRouteBuilt();
  bool const isRouteFinished = f.IsRouteFinished();
  bool const isRouteRebuildingOnly = f.IsRouteRebuildingOnly();
  bool const needGPSForRouting = ((isRouteBuilt || isRouteRebuildingOnly) && !isRouteFinished);
  if (needGPSForRouting)
    return YES;

  return NO;
}

NSString * const kLocationPermissionRequestedKey = @"kLocationPermissionRequestedKey";

BOOL isPermissionRequested()
{
  return [[NSUserDefaults standardUserDefaults] boolForKey:kLocationPermissionRequestedKey];
}

void setPermissionRequested()
{
  NSUserDefaults * ud = [NSUserDefaults standardUserDefaults];
  [ud setBool:YES forKey:kLocationPermissionRequestedKey];
  [ud synchronize];
}
}  // namespace

@interface MWMLocationManager ()<CLLocationManagerDelegate>

@property(nonatomic) BOOL started;
@property(nonatomic) CLLocationManager * locationManager;
@property(nonatomic) GeoMode geoMode;
@property(nonatomic) CLHeading * lastHeadingInfo;
@property(nonatomic) CLLocation * lastLocationInfo;
@property(nonatomic) location::TLocationError lastLocationStatus;
@property(nonatomic) MWMLocationPredictor * predictor;
@property(nonatomic) TObservers * observers;
@property(nonatomic) MWMLocationFrameworkUpdate frameworkUpdateMode;
@property(nonatomic) location::TLocationSource locationSource;

@end

@implementation MWMLocationManager

#pragma mark - Init

+ (MWMLocationManager *)manager
{
  static MWMLocationManager * manager;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    manager = [[super alloc] initManager];
  });
  return manager;
}

- (instancetype)initManager
{
  self = [super init];
  if (self)
    _observers = [TObservers weakObjectsHashTable];
  return self;
}

- (void)dealloc
{
  self.locationManager.delegate = nil;
}
+ (void)start { [self manager].started = YES; }
#pragma mark - Add/Remove Observers

+ (void)addObserver:(TObserver)observer
{
  runAsyncOnMainQueue(^{
    MWMLocationManager * manager = [self manager];
    [manager.observers addObject:observer];
    [manager processLocationUpdate:manager.lastLocationInfo];
  });
}

+ (void)removeObserver:(TObserver)observer
{
  runAsyncOnMainQueue(^{
    [[self manager].observers removeObject:observer];
  });
}

#pragma mark - App Life Cycle

+ (void)applicationDidBecomeActive
{<commit_msg>[crash-fix] [ios] Attempt to fix crash.<commit_after>#import "MWMLocationManager.h"
#import <Pushwoosh/PushNotificationManager.h>
#import "MWMAlertViewController.h"
#import "MWMCommon.h"
#import "MWMController.h"
#import "MWMLocationObserver.h"
#import "MWMLocationPredictor.h"
#import "MapsAppDelegate.h"
#import "Statistics.h"

#import "3party/Alohalytics/src/alohalytics_objc.h"

#include "Framework.h"

#include "map/gps_tracker.hpp"

#include "std/map.hpp"

namespace
{
using TObserver = id<MWMLocationObserver>;
using TObservers = NSHashTable<__kindof TObserver>;

location::GpsInfo gpsInfoFromLocation(CLLocation * l, location::TLocationSource source)
{
  location::GpsInfo info;
  info.m_source = source;

  info.m_latitude = l.coordinate.latitude;
  info.m_longitude = l.coordinate.longitude;
  info.m_timestamp = l.timestamp.timeIntervalSince1970;

  if (l.horizontalAccuracy >= 0.0)
    info.m_horizontalAccuracy = l.horizontalAccuracy;

  if (l.verticalAccuracy >= 0.0)
  {
    info.m_verticalAccuracy = l.verticalAccuracy;
    info.m_altitude = l.altitude;
  }

  if (l.course >= 0.0)
    info.m_bearing = l.course;

  if (l.speed >= 0.0)
    info.m_speed = l.speed;
  return info;
}

location::CompassInfo compassInfoFromHeading(CLHeading * h)
{
  location::CompassInfo info;
  if (h.trueHeading >= 0.0)
    info.m_bearing = my::DegToRad(h.trueHeading);
  else if (h.headingAccuracy >= 0.0)
    info.m_bearing = my::DegToRad(h.magneticHeading);
  return info;
}

typedef NS_OPTIONS(NSUInteger, MWMLocationFrameworkUpdate) {
  MWMLocationFrameworkUpdateNone = 0,
  MWMLocationFrameworkUpdateLocation = 1 << 0,
  MWMLocationFrameworkUpdateHeading = 1 << 1,
  MWMLocationFrameworkUpdateStatus = 1 << 2
};

enum class GeoMode
{
  Pending,
  InPosition,
  NotInPosition,
  FollowAndRotate,
  VehicleRouting,
  PedestrianRouting,
  BicycleRouting
};

struct DesiredAccuracy
{
  CLLocationAccuracy charging;
  CLLocationAccuracy battery;
};

struct GeoModeSettings
{
  CLLocationDistance distanceFilter;
  DesiredAccuracy accuracy;
};

map<GeoMode, GeoModeSettings> const kGeoSettings{
    {GeoMode::Pending,
     {.distanceFilter = kCLDistanceFilterNone,
      .accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
                   .battery = kCLLocationAccuracyBestForNavigation}}},
    {GeoMode::InPosition,
     {.distanceFilter = 2,
      .accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
                   .battery = kCLLocationAccuracyBest}}},
    {GeoMode::NotInPosition,
     {.distanceFilter = 5,
      .accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
                   .battery = kCLLocationAccuracyBest}}},
    {GeoMode::FollowAndRotate,
     {.distanceFilter = 2,
      .accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
                   .battery = kCLLocationAccuracyBest}}},
    {GeoMode::VehicleRouting,
     {.distanceFilter = kCLDistanceFilterNone,
      .accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
                   .battery = kCLLocationAccuracyBest}}},
    {GeoMode::PedestrianRouting,
     {.distanceFilter = 2,
      .accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
                   .battery = kCLLocationAccuracyBest}}},
    {GeoMode::BicycleRouting,
     {.distanceFilter = 2,
      .accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
                   .battery = kCLLocationAccuracyBest}}}};

BOOL keepRunningInBackground()
{
  bool const needGPSForTrackRecorder = GpsTracker::Instance().IsEnabled();
  if (needGPSForTrackRecorder)
    return YES;

  auto const & f = GetFramework();
  bool const isRouteBuilt = f.IsRouteBuilt();
  bool const isRouteFinished = f.IsRouteFinished();
  bool const isRouteRebuildingOnly = f.IsRouteRebuildingOnly();
  bool const needGPSForRouting = ((isRouteBuilt || isRouteRebuildingOnly) && !isRouteFinished);
  if (needGPSForRouting)
    return YES;

  return NO;
}

NSString * const kLocationPermissionRequestedKey = @"kLocationPermissionRequestedKey";

BOOL isPermissionRequested()
{
  return [[NSUserDefaults standardUserDefaults] boolForKey:kLocationPermissionRequestedKey];
}

void setPermissionRequested()
{
  NSUserDefaults * ud = [NSUserDefaults standardUserDefaults];
  [ud setBool:YES forKey:kLocationPermissionRequestedKey];
  [ud synchronize];
}
}  // namespace

@interface MWMLocationManager ()<CLLocationManagerDelegate>

@property(nonatomic) BOOL started;
@property(nonatomic) CLLocationManager * locationManager;
@property(nonatomic) GeoMode geoMode;
@property(nonatomic) CLHeading * lastHeadingInfo;
@property(nonatomic) CLLocation * lastLocationInfo;
@property(nonatomic) location::TLocationError lastLocationStatus;
@property(nonatomic) MWMLocationPredictor * predictor;
@property(nonatomic) TObservers * observers;
@property(nonatomic) MWMLocationFrameworkUpdate frameworkUpdateMode;
@property(nonatomic) location::TLocationSource locationSource;

@end

@implementation MWMLocationManager

#pragma mark - Init

+ (MWMLocationManager *)manager
{
  static MWMLocationManager * manager;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    manager = [[super alloc] initManager];
  });
  return manager;
}

- (instancetype)initManager
{
  self = [super init];
  if (self)
    _observers = [TObservers weakObjectsHashTable];
  return self;
}

- (void)dealloc
{
  [[NSNotificationCenter defaultCenter] removeObserver:self];
  self.locationManager.delegate = nil;
}
+ (void)start { [self manager].started = YES; }
#pragma mark - Add/Remove Observers

+ (void)addObserver:(TObserver)observer
{
  runAsyncOnMainQueue(^{
    MWMLocationManager * manager = [self manager];
    [manager.observers addObject:observer];
    [manager processLocationUpdate:manager.lastLocationInfo];
  });
}

+ (void)removeObserver:(TObserver)observer
{
  runAsyncOnMainQueue(^{
    [[self manager].observers removeObject:observer];
  });
}

#pragma mark - App Life Cycle

+ (void)applicationDidBecomeActive
{
{'fraction_non_alphanumeric': 0.10759387102205759, 'fraction_numerical': 0.004882976932143459, 'mean_word_length': 4.7637069383794275, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4433289', 'n_tokens_mistral': 3956, 'n_tokens_neox': 3767, 'n_words': 696}