WIP: Remove local ads - WIP DON'T MERGE #165
|
@ -306,7 +306,6 @@ add_subdirectory(generator/mwm_diff)
|
|||
add_subdirectory(geometry)
|
||||
add_subdirectory(indexer)
|
||||
add_subdirectory(kml)
|
||||
add_subdirectory(local_ads)
|
||||
add_subdirectory(map)
|
||||
add_subdirectory(metrics)
|
||||
add_subdirectory(partners_api)
|
||||
|
|
|
@ -41770,10 +41770,6 @@ colors {
|
|||
name: "RouteMaskPedestrian"
|
||||
color: 2147483648
|
||||
}
|
||||
value {
|
||||
name: "LocalAdsSecondaryTextOutline"
|
||||
color: 16777215
|
||||
}
|
||||
value {
|
||||
name: "BookmarkGreen"
|
||||
color: 3968060
|
||||
|
@ -41900,13 +41896,6 @@ colors {
|
|||
name: "TransitTransferInnerMarker"
|
||||
color: 16777215
|
||||
}
|
||||
value {
|
||||
name: "LocalAdsPrimaryText"
|
||||
}
|
||||
value {
|
||||
name: "LocalAdsPrimaryTextOutline"
|
||||
color: 16777215
|
||||
}
|
||||
value {
|
||||
name: "BookmarkPurple"
|
||||
color: 10167474
|
||||
|
@ -41942,9 +41931,6 @@ colors {
|
|||
name: "RouteMarkPrimaryTextOutline"
|
||||
color: 16777215
|
||||
}
|
||||
value {
|
||||
name: "LocalAdsSecondaryText"
|
||||
}
|
||||
value {
|
||||
name: "Route"
|
||||
color: 34815
|
||||
|
|
|
@ -42162,9 +42162,6 @@ colors {
|
|||
name: "RouteMaskPedestrian"
|
||||
color: 2147483648
|
||||
}
|
||||
value {
|
||||
name: "LocalAdsSecondaryTextOutline"
|
||||
}
|
||||
value {
|
||||
name: "BookmarkGreen"
|
||||
color: 3968060
|
||||
|
@ -42290,13 +42287,6 @@ colors {
|
|||
name: "TransitTransferInnerMarker"
|
||||
color: 8947848
|
||||
}
|
||||
value {
|
||||
name: "LocalAdsPrimaryText"
|
||||
color: 8947848
|
||||
}
|
||||
value {
|
||||
name: "LocalAdsPrimaryTextOutline"
|
||||
}
|
||||
value {
|
||||
name: "BookmarkPurple"
|
||||
color: 10167474
|
||||
|
@ -42332,10 +42322,6 @@ colors {
|
|||
value {
|
||||
name: "RouteMarkPrimaryTextOutline"
|
||||
}
|
||||
value {
|
||||
name: "LocalAdsSecondaryText"
|
||||
color: 8947848
|
||||
}
|
||||
value {
|
||||
name: "Route"
|
||||
color: 34815
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
02001_hospital-l
|
||||
02002_veterinary-l
|
||||
02003_pharmacy-l
|
||||
03001_restaurant-l
|
||||
03002_bar-l
|
||||
03003_beer-l
|
||||
03004_fastfood-l
|
||||
03005_cafe-l
|
||||
04001_tourism-l
|
||||
04002_viewpoint-l
|
||||
04004_museum-l
|
||||
04005_gallery-l
|
||||
04006_zoo-l
|
||||
04007_theatre-l
|
||||
04008_cinema-l
|
||||
04009_casino-l
|
||||
04010_toilets-l
|
||||
04021_stadium-l
|
||||
05001_hotel-l
|
||||
05002_motel-l
|
||||
05003_alpine_hut-l
|
||||
05004_caravan_site-l
|
||||
05005_apartment-l
|
||||
05006_hostel-l
|
||||
06001_convenience-l
|
||||
06002_marketplace-l
|
||||
06003_kiosk-l
|
||||
06004_grocery-l
|
||||
06005_shop-l
|
||||
06006_department_store-l
|
||||
06007_electronics-l
|
||||
06008_florist-l
|
||||
06009_shop-bicycle-l
|
||||
06010_jewelry-l
|
||||
06011_beauty-l
|
||||
06012_bakery-l
|
||||
06013_gift-l
|
||||
06014_furniture-l
|
||||
06015_garden_center-l
|
||||
06016_greengrocer-l
|
||||
06017_shoes-l
|
||||
06018_computer-l
|
||||
06019_hardware-l
|
||||
06020_doityourself-l
|
||||
06021_sweets-l
|
||||
06022_toys-l
|
||||
06023_butcher-l
|
||||
06024_mobile_phone-l
|
||||
06025_optician-l
|
||||
06026_sports-l
|
||||
06027_chemist-l
|
||||
06028_clothes-l
|
||||
06029_book-shop-l
|
||||
06030_petshop-l
|
||||
06031_photo-shop-l
|
||||
06032_copyshop-l
|
||||
06033_seafood-shop-l
|
||||
06034_ticket-shop-l
|
||||
06035_outdoor-shop-l
|
||||
06036_alcohol-l
|
||||
06037_hand-l
|
||||
06038_car_shop-l
|
||||
06039_motorcycle_shop-l
|
||||
06040_travel_agency_shop-l
|
||||
06041_stationery_shop-l
|
||||
06042_newsagent-l
|
||||
06043_ice_cream-l
|
||||
06044_funeral_directors-l
|
||||
06045_media-l
|
||||
06046_music-l
|
||||
06047_erotic-l
|
||||
07001_atm-l
|
||||
07002_bank-l
|
||||
07003_banknote-l
|
||||
07004_office-financial-l
|
||||
08001_fuel-l
|
||||
08002_charging-station-l
|
||||
08003_car-repair-l
|
||||
08004_tire-repair-l
|
||||
08005_car-sharing-l
|
||||
08006_car-part-l
|
||||
08007_car-wash-l
|
||||
09001_bicycle-l
|
||||
09002_swimming-l
|
||||
09003_baseball-l
|
||||
09004_tennis-l
|
||||
09005_basketball-l
|
||||
09006_america-football-l
|
||||
09007_soccer-l
|
||||
09008_golf-l
|
||||
09009_pitch-l
|
||||
09010_skiing-l
|
||||
09011_cricket-l
|
||||
09012_bowls-l
|
||||
09013_curling-l
|
||||
09014_diving-l
|
||||
09015_archery-l
|
||||
09016_australian-football-l
|
||||
09017_climbing-l
|
||||
09018_gym-l
|
||||
09019_equestrian-l
|
||||
09020_theme_park-l
|
||||
0_burger-king
|
||||
10001_kindergarten-l
|
||||
10002_school-l
|
||||
10003_college-l
|
||||
10004_library-l
|
||||
10005_mail-l
|
||||
10008_historic-ship-l
|
||||
10012_public-building-l
|
||||
10013_hairdresser-l
|
||||
10014_taxi-l
|
||||
10015_office-l
|
||||
10016_remains-l
|
||||
10017_lighthouse-l
|
||||
10022_laundry-l
|
||||
10024_dentist-l
|
||||
10025_bookmaker-l
|
||||
10026_lawyer-l
|
||||
10027_sauna-l
|
||||
10028_picnic-l
|
||||
10029_campsite-l
|
||||
10030_hunting-tower-l
|
||||
10031_bbq-l
|
||||
10032_cemetery-l
|
||||
10033_beach-l
|
||||
10034_dog_park-l
|
||||
10035_home-l
|
||||
10036_vending-l
|
Before Width: | Height: | Size: 321 KiB After Width: | Height: | Size: 321 KiB |
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 978 KiB |
Before Width: | Height: | Size: 317 KiB After Width: | Height: | Size: 317 KiB |
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 931 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 631 KiB After Width: | Height: | Size: 464 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 149 KiB |
Before Width: | Height: | Size: 555 KiB After Width: | Height: | Size: 447 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 275 KiB |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 360 KiB After Width: | Height: | Size: 262 KiB |
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 203 KiB |
Before Width: | Height: | Size: 903 KiB After Width: | Height: | Size: 637 KiB |
Before Width: | Height: | Size: 207 KiB After Width: | Height: | Size: 207 KiB |
Before Width: | Height: | Size: 847 KiB After Width: | Height: | Size: 609 KiB |
Before Width: | Height: | Size: 337 KiB After Width: | Height: | Size: 337 KiB |
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1 MiB |
Before Width: | Height: | Size: 336 KiB After Width: | Height: | Size: 336 KiB |
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 997 KiB |
Before Width: | Height: | Size: 434 KiB After Width: | Height: | Size: 434 KiB |
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 430 KiB After Width: | Height: | Size: 430 KiB |
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.2 MiB |
|
@ -114,7 +114,6 @@ Some of these contain their own README files.
|
|||
* `installer` - long-abandoned installer for Windows.
|
||||
* `iphone` - iOS UI.
|
||||
* `kml` - manipulation of KML files.
|
||||
* `local_ads` -
|
||||
* `mapshot` - generate screenshots of maps, specified by coordinates and zoom level.
|
||||
* `metrics` -
|
||||
* `openlr` -
|
||||
|
|
|
@ -27,7 +27,6 @@ Currently available modules and setup's:
|
|||
|-------------|----------------------------------------|
|
||||
| pygen | generator/pygen/setup.py |
|
||||
| pykmlib | kml/pykmlib/setup.py |
|
||||
| pylocal_ads | local_ads/pylocal_ads/setup.py |
|
||||
| pymvm_diff | generator/mwm_diff/pymwm_diff/setup.py |
|
||||
| pysearch | search/pysearch/setup.py |
|
||||
| pytracking | tracking/pytracking/setup.py |
|
||||
|
|
|
@ -42,7 +42,7 @@ float const kGlyphAreaMultiplier = 1.2f;
|
|||
float const kGlyphAreaCoverage = 0.9f;
|
||||
|
||||
std::string const kDefaultSymbolsTexture = "symbols";
|
||||
std::string const kSymbolTextures[] = { kDefaultSymbolsTexture, "symbols-ad" };
|
||||
std::string const kSymbolTextures[] = { kDefaultSymbolsTexture };
|
||||
uint32_t const kDefaultSymbolsIndex = 0;
|
||||
|
||||
namespace
|
||||
|
|
|
@ -1493,12 +1493,6 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView, bool activeFram
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
StencilWriterGuard guard(make_ref(m_postprocessRenderer), m_context);
|
||||
RenderOverlayLayer(modelView);
|
||||
RenderUserMarksLayer(modelView, DepthLayer::LocalAdsMarkLayer);
|
||||
}
|
||||
|
||||
m_gpsTrackRenderer->RenderTrack(m_context, make_ref(m_gpuProgramManager), modelView, m_currentZoomLevel,
|
||||
m_frameValues);
|
||||
|
||||
|
@ -1886,7 +1880,6 @@ void FrontendRenderer::RenderFrame()
|
|||
void FrontendRenderer::BuildOverlayTree(ScreenBase const & modelView)
|
||||
{
|
||||
static std::vector<DepthLayer> layers = {DepthLayer::OverlayLayer,
|
||||
DepthLayer::LocalAdsMarkLayer,
|
||||
DepthLayer::NavigationLayer,
|
||||
DepthLayer::RoutingBottomMarkLayer,
|
||||
DepthLayer::RoutingMarkLayer};
|
||||
|
|
|
@ -151,7 +151,6 @@ bool RenderGroup::IsUserMark() const
|
|||
depthLayer == DepthLayer::RoutingMarkLayer ||
|
||||
depthLayer == DepthLayer::GuidesBottomMarkLayer ||
|
||||
depthLayer == DepthLayer::GuidesMarkLayer ||
|
||||
depthLayer == DepthLayer::LocalAdsMarkLayer ||
|
||||
depthLayer == DepthLayer::SearchMarkLayer;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ std::array<drape_ptr<RenderStateExtension>, static_cast<size_t>(DepthLayer::Laye
|
|||
make_unique_dp<RenderStateExtension>(DepthLayer::Geometry3dLayer),
|
||||
make_unique_dp<RenderStateExtension>(DepthLayer::UserLineLayer),
|
||||
make_unique_dp<RenderStateExtension>(DepthLayer::OverlayLayer),
|
||||
make_unique_dp<RenderStateExtension>(DepthLayer::LocalAdsMarkLayer),
|
||||
make_unique_dp<RenderStateExtension>(DepthLayer::TransitSchemeLayer),
|
||||
make_unique_dp<RenderStateExtension>(DepthLayer::UserMarkLayer),
|
||||
make_unique_dp<RenderStateExtension>(DepthLayer::NavigationLayer),
|
||||
|
|
|
@ -16,7 +16,6 @@ enum class DepthLayer : uint8_t
|
|||
Geometry3dLayer,
|
||||
UserLineLayer,
|
||||
OverlayLayer,
|
||||
LocalAdsMarkLayer,
|
||||
TransitSchemeLayer,
|
||||
UserMarkLayer,
|
||||
NavigationLayer,
|
||||
|
@ -60,7 +59,6 @@ inline std::string DebugPrint(DepthLayer layer)
|
|||
case DepthLayer::Geometry3dLayer: return "Geometry3dLayer";
|
||||
case DepthLayer::UserLineLayer: return "UserLineLayer";
|
||||
case DepthLayer::OverlayLayer: return "OverlayLayer";
|
||||
case DepthLayer::LocalAdsMarkLayer: return "LocalAdsMarkLayer";
|
||||
case DepthLayer::TransitSchemeLayer: return "TransitSchemeLayer";
|
||||
case DepthLayer::UserMarkLayer: return "UserMarkLayer";
|
||||
case DepthLayer::NavigationLayer: return "NavigationLayer";
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, PlacePageDataLocalAdsStatus) {
|
||||
PlacePageDataLocalAdsStatusNotAvailable,
|
||||
PlacePageDataLocalAdsStatusCandidate,
|
||||
PlacePageDataLocalAdsStatusCustomer,
|
||||
PlacePageDataLocalAdsStatusHidden
|
||||
};
|
||||
|
||||
@class OpeningHours;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
@ -24,9 +17,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property(nonatomic, readonly, nullable) NSString *address;
|
||||
@property(nonatomic, readonly, nullable) NSString *rawCoordinates;
|
||||
@property(nonatomic, readonly, nullable) NSString *formattedCoordinates;
|
||||
@property(nonatomic, readonly, nullable) NSString *localAdsUrl;
|
||||
@property(nonatomic, readonly) BOOL wifiAvailable;
|
||||
@property(nonatomic, readonly) PlacePageDataLocalAdsStatus localAdsStatus;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -7,19 +7,6 @@
|
|||
using namespace place_page;
|
||||
using namespace osm;
|
||||
|
||||
static PlacePageDataLocalAdsStatus convertLocalAdsStatus(LocalAdsStatus status) {
|
||||
switch (status) {
|
||||
case LocalAdsStatus::NotAvailable:
|
||||
return PlacePageDataLocalAdsStatusNotAvailable;
|
||||
case LocalAdsStatus::Candidate:
|
||||
return PlacePageDataLocalAdsStatusCandidate;
|
||||
case LocalAdsStatus::Customer:
|
||||
return PlacePageDataLocalAdsStatusCustomer;
|
||||
case LocalAdsStatus::Hidden:
|
||||
return PlacePageDataLocalAdsStatusHidden;
|
||||
}
|
||||
}
|
||||
|
||||
@implementation PlacePageInfoData
|
||||
|
||||
@end
|
||||
|
@ -71,8 +58,6 @@ static PlacePageDataLocalAdsStatus convertLocalAdsStatus(LocalAdsStatus status)
|
|||
_address = rawData.GetAddress().empty() ? nil : @(rawData.GetAddress().c_str());
|
||||
_rawCoordinates = @(rawData.GetFormattedCoordinate(true).c_str());
|
||||
_formattedCoordinates = @(rawData.GetFormattedCoordinate(false).c_str());
|
||||
_localAdsStatus = convertLocalAdsStatus(rawData.GetLocalAdsStatus());
|
||||
_localAdsUrl = rawData.GetLocalAdsUrl().empty() ? nil : @(rawData.GetLocalAdsUrl().c_str());
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#import "MWMDiscoveryController.h"
|
||||
#import "MWMEditorHelper.h"
|
||||
#import "MWMFrameworkListener.h"
|
||||
#import "MWMGeoTrackerCore.h"
|
||||
#import "MWMKeyboard.h"
|
||||
#import "MWMLocationManager.h"
|
||||
#import "MWMLocationModeListener.h"
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
final class Geo: NSObject {
|
||||
@objc
|
||||
static func geoTracker() -> IGeoTracker {
|
||||
let trackerCore = MWMGeoTrackerCore()
|
||||
return GeoTracker(trackerCore: trackerCore)
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
@objc
|
||||
protocol IGeoTracker: AnyObject {
|
||||
func startTracking()
|
||||
func endTracking()
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
#import "IMWMGeoTrackerZone.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol IMWMGeoTrackerCore
|
||||
|
||||
- (NSArray<id<IMWMGeoTrackerZone>> *)geoZonesForLat:(CLLocationDegrees)lat
|
||||
lon:(CLLocationDegrees)lon
|
||||
accuracy:(CLLocationAccuracy) accuracy;
|
||||
|
||||
- (void)logEnterZone:(id<IMWMGeoTrackerZone>)zone location:(CLLocation *)location;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,11 +0,0 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol IMWMGeoTrackerZone<NSObject>
|
||||
|
||||
@property (nonatomic, readonly) CLLocationDegrees latitude;
|
||||
@property (nonatomic, readonly) CLLocationDegrees longitude;
|
||||
@property (nonatomic, readonly) NSString *identifier;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,66 +0,0 @@
|
|||
class GeoTracker: NSObject, IGeoTracker {
|
||||
private let trackerCore: IMWMGeoTrackerCore
|
||||
private let locationManager = CLLocationManager()
|
||||
private var trackingZones: [String : IMWMGeoTrackerZone]?
|
||||
private var zoneTrackers: [String : GeoZoneTracker] = [:]
|
||||
|
||||
init(trackerCore: IMWMGeoTrackerCore) {
|
||||
self.trackerCore = trackerCore
|
||||
super.init()
|
||||
locationManager.delegate = self
|
||||
}
|
||||
|
||||
deinit {
|
||||
locationManager.delegate = nil
|
||||
}
|
||||
|
||||
@objc
|
||||
func startTracking() {
|
||||
if CLLocationManager.significantLocationChangeMonitoringAvailable() {
|
||||
locationManager.startMonitoringSignificantLocationChanges()
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
func endTracking() {
|
||||
locationManager.stopMonitoringSignificantLocationChanges()
|
||||
}
|
||||
}
|
||||
|
||||
extension GeoTracker: CLLocationManagerDelegate {
|
||||
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
||||
if let location = locations.last {
|
||||
locationManager.monitoredRegions.forEach {
|
||||
locationManager.stopMonitoring(for: $0)
|
||||
}
|
||||
|
||||
if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
|
||||
let zones = trackerCore.geoZones(forLat: location.coordinate.latitude,
|
||||
lon: location.coordinate.longitude,
|
||||
accuracy: location.horizontalAccuracy)
|
||||
zones.forEach {
|
||||
locationManager.startMonitoring(
|
||||
for: CLCircularRegion(center: CLLocationCoordinate2DMake($0.latitude, $0.longitude),
|
||||
radius: 100,
|
||||
identifier: $0.identifier))
|
||||
}
|
||||
trackingZones = zones.reduce(into: [:]) { $0[$1.identifier] = $1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
|
||||
if let zone = trackingZones?[region.identifier] {
|
||||
let zoneTracker = GeoZoneTracker(zone, trackerCore: trackerCore)
|
||||
zoneTracker.startTracking()
|
||||
zoneTrackers[region.identifier] = zoneTracker
|
||||
}
|
||||
}
|
||||
|
||||
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
|
||||
if let zoneTracker = zoneTrackers[region.identifier] {
|
||||
zoneTracker.stopTracking()
|
||||
zoneTrackers[region.identifier] = nil
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
class GeoZoneTracker: NSObject {
|
||||
let geoZone: IMWMGeoTrackerZone
|
||||
let trackerCore: IMWMGeoTrackerCore
|
||||
let locationManager = CLLocationManager()
|
||||
|
||||
init(_ zone: IMWMGeoTrackerZone, trackerCore: IMWMGeoTrackerCore) {
|
||||
self.geoZone = zone
|
||||
self.trackerCore = trackerCore
|
||||
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
|
||||
locationManager.distanceFilter = 10
|
||||
super.init()
|
||||
locationManager.delegate = self
|
||||
}
|
||||
|
||||
deinit {
|
||||
stopTracking()
|
||||
locationManager.delegate = nil
|
||||
}
|
||||
|
||||
func startTracking() {
|
||||
locationManager.startUpdatingLocation()
|
||||
}
|
||||
|
||||
func stopTracking() {
|
||||
locationManager.stopUpdatingLocation()
|
||||
}
|
||||
}
|
||||
|
||||
extension GeoZoneTracker: CLLocationManagerDelegate {
|
||||
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
||||
if let visitedLocation = locations.filter({
|
||||
$0.horizontalAccuracy <= 30 &&
|
||||
$0.distance(from: CLLocation(latitude: geoZone.latitude, longitude: geoZone.longitude)) <= 20
|
||||
}).first {
|
||||
trackerCore.logEnter(geoZone, location: visitedLocation)
|
||||
stopTracking()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
#import "IMWMGeoTrackerCore.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MWMGeoTrackerCore : NSObject<IMWMGeoTrackerCore>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,74 +0,0 @@
|
|||
#import "MWMGeoTrackerCore.h"
|
||||
|
||||
#include "map/framework_light.hpp"
|
||||
|
||||
@interface MWMGeoTrackerZone: NSObject<IMWMGeoTrackerZone>
|
||||
|
||||
@property (nonatomic, readwrite) NSString *identifier;
|
||||
|
||||
- (instancetype)initWithCampaignFeature:(CampaignFeature const &)feature;
|
||||
|
||||
- (CampaignFeature const &)feature;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMGeoTrackerZone {
|
||||
CampaignFeature _feature;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCampaignFeature:(CampaignFeature const &)feature {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_feature = feature;
|
||||
_identifier = [NSUUID UUID].UUIDString;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CLLocationDegrees)latitude {
|
||||
return _feature.m_lat;
|
||||
}
|
||||
|
||||
- (CLLocationDegrees)longitude {
|
||||
return _feature.m_lon;
|
||||
}
|
||||
|
||||
- (CampaignFeature const &)feature {
|
||||
return _feature;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMGeoTrackerCore
|
||||
|
||||
- (NSArray<id<IMWMGeoTrackerZone>> *)geoZonesForLat:(CLLocationDegrees)lat
|
||||
lon:(CLLocationDegrees)lon
|
||||
accuracy:(CLLocationAccuracy)accuracy {
|
||||
lightweight::Framework f(lightweight::REQUEST_TYPE_LOCAL_ADS_FEATURES);
|
||||
auto campainFeatures = f.GetLocalAdsFeatures(lat, lon, accuracy, 20);
|
||||
NSMutableArray * result = [NSMutableArray array];
|
||||
for (auto const & cf : campainFeatures) {
|
||||
[result addObject:[[MWMGeoTrackerZone alloc] initWithCampaignFeature:cf]];
|
||||
}
|
||||
return [result copy];
|
||||
}
|
||||
|
||||
- (void)logEnterZone:(id<IMWMGeoTrackerZone>)zone location:(CLLocation *)location {
|
||||
NSAssert([zone isKindOfClass:MWMGeoTrackerZone.class], @"zone must be of MWMGeoTrackerZone type");
|
||||
MWMGeoTrackerZone * geoZone = (MWMGeoTrackerZone *)zone;
|
||||
auto feature = geoZone.feature;
|
||||
lightweight::Framework f(lightweight::REQUEST_TYPE_LOCAL_ADS_STATISTICS);
|
||||
local_ads::Event event(local_ads::EventType::Visit,
|
||||
feature.m_mwmVersion,
|
||||
feature.m_countryId,
|
||||
feature.m_featureIndex,
|
||||
0,
|
||||
local_ads::Clock::now(),
|
||||
location.coordinate.latitude,
|
||||
location.coordinate.longitude,
|
||||
location.horizontalAccuracy);
|
||||
f.GetLocalAdsStatistics()->RegisterEvent(std::move(event));
|
||||
}
|
||||
|
||||
@end
|
|
@ -129,7 +129,6 @@ void setShowLocationAlert(BOOL needShow) {
|
|||
@property(nonatomic) Observers * observers;
|
||||
@property(nonatomic) MWMLocationFrameworkUpdate frameworkUpdateMode;
|
||||
@property(nonatomic) location::TLocationSource locationSource;
|
||||
@property(nonatomic) id<IGeoTracker> geoTracker;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -153,7 +152,6 @@ void setShowLocationAlert(BOOL needShow) {
|
|||
if (self)
|
||||
{
|
||||
_observers = [Observers weakObjectsHashTable];
|
||||
_geoTracker = [Geo geoTracker];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -479,7 +477,6 @@ void setShowLocationAlert(BOOL needShow) {
|
|||
if ([locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
|
||||
[locationManager requestAlwaysAuthorization];
|
||||
[locationManager startUpdatingLocation];
|
||||
[self.geoTracker startTracking];
|
||||
setPermissionRequested();
|
||||
if ([CLLocationManager headingAvailable])
|
||||
[locationManager startUpdatingHeading];
|
||||
|
|
|
@ -78,7 +78,6 @@
|
|||
341F09841C20138100F18AC5 /* libpugixml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 341F09831C20138100F18AC5 /* libpugixml.a */; };
|
||||
34201E091DC0DC7300D24118 /* libpartners_api.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DDB4BC31DAB98F000F4D021 /* libpartners_api.a */; };
|
||||
34201E0C1DC0E33100D24118 /* libtracking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 34201E0B1DC0E33100D24118 /* libtracking.a */; };
|
||||
342639361EA0E60A0025EB89 /* local_ads_symbols.txt in Resources */ = {isa = PBXBuildFile; fileRef = 450703081E9E6CF000E8C029 /* local_ads_symbols.txt */; };
|
||||
342CC5F21C2D7730005F3FE5 /* MWMAuthorizationLoginViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 342CC5F01C2D7730005F3FE5 /* MWMAuthorizationLoginViewController.mm */; };
|
||||
342EE4121C43DAA7009F6A49 /* MWMAuthorizationWebViewLoginViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 342EE4101C43DAA7009F6A49 /* MWMAuthorizationWebViewLoginViewController.mm */; };
|
||||
343064411E9FDC7300DC7665 /* SearchIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3430643F1E9FDC7300DC7665 /* SearchIndex.swift */; };
|
||||
|
@ -287,7 +286,6 @@
|
|||
4586D0E71F4813AB00DF9CE5 /* libmwm_diff.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4586D0E61F4813AB00DF9CE5 /* libmwm_diff.a */; };
|
||||
4598438621394CFD00F8CAB2 /* MetalPerformanceShaders.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4598438521394CFD00F8CAB2 /* MetalPerformanceShaders.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
45CBCCBA20590AAB006B55C2 /* libkml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45CBCCBB20590AAB006B55C2 /* libkml.a */; };
|
||||
45FFD65D1E965EBE00DB854E /* liblocal_ads.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45FFD65C1E965EBE00DB854E /* liblocal_ads.a */; };
|
||||
4701A93D243A917900B87683 /* TouchTransparentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4701A93C243A917800B87683 /* TouchTransparentView.swift */; };
|
||||
4707E4B12372FE860017DF6E /* PlacePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4707E4AF2372FE860017DF6E /* PlacePageViewController.swift */; };
|
||||
4707E4B42372FF480017DF6E /* PlacePage.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4707E4B32372FF480017DF6E /* PlacePage.storyboard */; };
|
||||
|
@ -369,11 +367,6 @@
|
|||
47A6F3C4235F47B90053FBA4 /* CitySubscriptionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 47A6F3C1235F47B90053FBA4 /* CitySubscriptionViewController.xib */; };
|
||||
47A6F3C5235F47B90053FBA4 /* BookmarksSubscriptionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47A6F3C2235F47B90053FBA4 /* BookmarksSubscriptionButton.swift */; };
|
||||
47AEF8402231249E00D20538 /* categories_brands.txt in Resources */ = {isa = PBXBuildFile; fileRef = 47AEF83F2231249E00D20538 /* categories_brands.txt */; };
|
||||
47B06DED21B696C20094CCAD /* GeoTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B06DEC21B696C20094CCAD /* GeoTracker.swift */; };
|
||||
47B06DF021B697230094CCAD /* MWMGeoTrackerCore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 47B06DEF21B697230094CCAD /* MWMGeoTrackerCore.mm */; };
|
||||
47B06DF921B95F5E0094CCAD /* IGeoTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B06DF821B95F5E0094CCAD /* IGeoTracker.swift */; };
|
||||
47B06DFE21B965950094CCAD /* Geo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B06DFD21B965950094CCAD /* Geo.swift */; };
|
||||
47B06E0021BAAC270094CCAD /* GeoZoneTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47B06DFF21BAAC270094CCAD /* GeoZoneTracker.swift */; };
|
||||
47B505542136B0C2009CBB55 /* DiscoveryTutorialBlur.xib in Resources */ = {isa = PBXBuildFile; fileRef = 47B505522136AB41009CBB55 /* DiscoveryTutorialBlur.xib */; };
|
||||
47B505552136B0CF009CBB55 /* SearchTutorialBlur.xib in Resources */ = {isa = PBXBuildFile; fileRef = 47B505532136AD69009CBB55 /* SearchTutorialBlur.xib */; };
|
||||
47B9065221C7FA400079C85E /* MWMWebImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 47B9064921C7FA3B0079C85E /* MWMWebImage.m */; };
|
||||
|
@ -1377,7 +1370,6 @@
|
|||
408645FB21495EB1000A4A1D /* categories_cuisines.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = categories_cuisines.txt; path = ../../data/categories_cuisines.txt; sourceTree = "<group>"; };
|
||||
4501B1922077C35A001B9173 /* resources-xxxhdpi_clear */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "resources-xxxhdpi_clear"; path = "../../data/resources-xxxhdpi_clear"; sourceTree = "<group>"; };
|
||||
4501B1932077C35A001B9173 /* resources-xxxhdpi_dark */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "resources-xxxhdpi_dark"; path = "../../data/resources-xxxhdpi_dark"; sourceTree = "<group>"; };
|
||||
450703081E9E6CF000E8C029 /* local_ads_symbols.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = local_ads_symbols.txt; path = ../../data/local_ads_symbols.txt; sourceTree = "<group>"; };
|
||||
450B5C822355F50200E9019E /* libweb_api.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libweb_api.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
451950391B7A3E070085DA05 /* patterns.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = patterns.txt; path = ../../data/patterns.txt; sourceTree = "<group>"; };
|
||||
452FCA3A1B6A3DF7007019AB /* colors.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = colors.txt; path = ../../data/colors.txt; sourceTree = "<group>"; };
|
||||
|
@ -1391,7 +1383,6 @@
|
|||
4598438521394CFD00F8CAB2 /* MetalPerformanceShaders.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalPerformanceShaders.framework; path = System/Library/Frameworks/MetalPerformanceShaders.framework; sourceTree = SDKROOT; };
|
||||
4598438921394D7700F8CAB2 /* shaders_metal.metallib */ = {isa = PBXFileReference; explicitFileType = "archive.metal-library"; path = shaders_metal.metallib; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
45CBCCBB20590AAB006B55C2 /* libkml.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libkml.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
45FFD65C1E965EBE00DB854E /* liblocal_ads.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = liblocal_ads.a; path = "/Users/r.kuznetsov/Dev/Projects/omim/xcode/local_ads/../../../omim-build/xcode/Debug/liblocal_ads.a"; sourceTree = "<absolute>"; };
|
||||
46F26CD610F623BA00ECCA39 /* EAGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = EAGLView.h; sourceTree = "<group>"; };
|
||||
46F26CD710F623BA00ECCA39 /* EAGLView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = EAGLView.mm; sourceTree = "<group>"; };
|
||||
46F8A2EB10EB63040045521A /* MapViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = MapViewController.h; sourceTree = "<group>"; };
|
||||
|
@ -1481,14 +1472,6 @@
|
|||
47A6F3C1235F47B90053FBA4 /* CitySubscriptionViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CitySubscriptionViewController.xib; sourceTree = "<group>"; };
|
||||
47A6F3C2235F47B90053FBA4 /* BookmarksSubscriptionButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksSubscriptionButton.swift; sourceTree = "<group>"; };
|
||||
47AEF83F2231249E00D20538 /* categories_brands.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = categories_brands.txt; path = ../../data/categories_brands.txt; sourceTree = "<group>"; };
|
||||
47B06DEC21B696C20094CCAD /* GeoTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoTracker.swift; sourceTree = "<group>"; };
|
||||
47B06DEE21B697230094CCAD /* MWMGeoTrackerCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMGeoTrackerCore.h; sourceTree = "<group>"; };
|
||||
47B06DEF21B697230094CCAD /* MWMGeoTrackerCore.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMGeoTrackerCore.mm; sourceTree = "<group>"; };
|
||||
47B06DF821B95F5E0094CCAD /* IGeoTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IGeoTracker.swift; sourceTree = "<group>"; };
|
||||
47B06DFA21B960080094CCAD /* IMWMGeoTrackerCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IMWMGeoTrackerCore.h; sourceTree = "<group>"; };
|
||||
47B06DFB21B960CE0094CCAD /* IMWMGeoTrackerZone.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IMWMGeoTrackerZone.h; sourceTree = "<group>"; };
|
||||
47B06DFD21B965950094CCAD /* Geo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Geo.swift; sourceTree = "<group>"; };
|
||||
47B06DFF21BAAC270094CCAD /* GeoZoneTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoZoneTracker.swift; sourceTree = "<group>"; };
|
||||
47B505522136AB41009CBB55 /* DiscoveryTutorialBlur.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DiscoveryTutorialBlur.xib; sourceTree = "<group>"; };
|
||||
47B505532136AD69009CBB55 /* SearchTutorialBlur.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SearchTutorialBlur.xib; sourceTree = "<group>"; };
|
||||
47B9064921C7FA3B0079C85E /* MWMWebImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MWMWebImage.m; sourceTree = "<group>"; };
|
||||
|
@ -2180,7 +2163,6 @@
|
|||
34E6F2DB1F459C05008E14F9 /* GLKit.framework in Frameworks */,
|
||||
F6F8E3C51EF8469700F2DE8F /* libugc.a in Frameworks */,
|
||||
3488B03B1E9D13EF0068AFD8 /* UserNotifications.framework in Frameworks */,
|
||||
45FFD65D1E965EBE00DB854E /* liblocal_ads.a in Frameworks */,
|
||||
34D1B6F11E95096B0057E9C7 /* libicu.a in Frameworks */,
|
||||
671E78D31E6A423300B2859B /* librouting_common.a in Frameworks */,
|
||||
67B78B471E422E0A0018E590 /* MobileCoreServices.framework in Frameworks */,
|
||||
|
@ -2370,7 +2352,6 @@
|
|||
34D1B6F01E95096B0057E9C7 /* libicu.a */,
|
||||
6741AAAE1BF356B9002C974C /* libindexer.a */,
|
||||
6741AAAF1BF356B9002C974C /* libjansson.a */,
|
||||
45FFD65C1E965EBE00DB854E /* liblocal_ads.a */,
|
||||
6741AAB11BF356B9002C974C /* libmap.a */,
|
||||
340DC82B1C4E72C700EAA2CC /* liboauthcpp.a */,
|
||||
6741AAB31BF356B9002C974C /* libopening_hours.a */,
|
||||
|
@ -2520,7 +2501,6 @@
|
|||
340475291E081A4600C92850 /* Location */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
47B06DEB21B6962D0094CCAD /* GeoTracker */,
|
||||
3404752A1E081A4600C92850 /* MWMLocationHelpers.h */,
|
||||
3404752B1E081A4600C92850 /* MWMLocationManager.h */,
|
||||
3404752C1E081A4600C92850 /* MWMLocationManager.mm */,
|
||||
|
@ -3389,29 +3369,6 @@
|
|||
path = Metrics;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
47B06DEB21B6962D0094CCAD /* GeoTracker */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
47B06DFD21B965950094CCAD /* Geo.swift */,
|
||||
47B06DFC21B965450094CCAD /* Impl */,
|
||||
47B06DF821B95F5E0094CCAD /* IGeoTracker.swift */,
|
||||
47B06DFB21B960CE0094CCAD /* IMWMGeoTrackerZone.h */,
|
||||
47B06DFA21B960080094CCAD /* IMWMGeoTrackerCore.h */,
|
||||
);
|
||||
path = GeoTracker;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
47B06DFC21B965450094CCAD /* Impl */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
47B06DEC21B696C20094CCAD /* GeoTracker.swift */,
|
||||
47B06DFF21BAAC270094CCAD /* GeoZoneTracker.swift */,
|
||||
47B06DEE21B697230094CCAD /* MWMGeoTrackerCore.h */,
|
||||
47B06DEF21B697230094CCAD /* MWMGeoTrackerCore.mm */,
|
||||
);
|
||||
path = Impl;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
47B9064821C7FA100079C85E /* WebImage */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -4864,7 +4821,6 @@
|
|||
EEFE7C1212F8C9E1006AF8C3 /* fonts_blacklist.txt */,
|
||||
EEFE7C1312F8C9E1006AF8C3 /* fonts_whitelist.txt */,
|
||||
BB7626B41E8559980031D71C /* icudt57l.dat */,
|
||||
450703081E9E6CF000E8C029 /* local_ads_symbols.txt */,
|
||||
F623DA6A1C9C2731006A3436 /* opening_hours_how_to_edit.html */,
|
||||
FA85F632145DDDC20090E1A0 /* packed_polygons.bin */,
|
||||
451950391B7A3E070085DA05 /* patterns.txt */,
|
||||
|
@ -5119,7 +5075,6 @@
|
|||
F6E2FDF51E097BA00083EBEC /* MWMOpeningHoursAddScheduleTableViewCell.xib in Resources */,
|
||||
CD96C70D22A681C400DB7CFE /* DiscoveryGuideCell.xib in Resources */,
|
||||
F6E2FDFB1E097BA00083EBEC /* MWMOpeningHoursAllDayTableViewCell.xib in Resources */,
|
||||
342639361EA0E60A0025EB89 /* local_ads_symbols.txt in Resources */,
|
||||
4554B6EC1E55F0EF0084017F /* drules_proto_vehicle_clear.bin in Resources */,
|
||||
47CA68F2250B54AF00671019 /* BookmarkCell.xib in Resources */,
|
||||
337F98A321D37B5800C8AC27 /* SearchHistoryViewController.xib in Resources */,
|
||||
|
@ -5353,7 +5308,6 @@
|
|||
993DF12123F6BDB100AC231A /* UIViewControllerRenderer.swift in Sources */,
|
||||
34D3AFF61E37A36A004100F9 /* UICollectionView+Cells.swift in Sources */,
|
||||
4767CDA420AAF66B00BD8166 /* NSAttributedString+HTML.swift in Sources */,
|
||||
47B06DFE21B965950094CCAD /* Geo.swift in Sources */,
|
||||
6741A9A91BF340DE002C974C /* MWMDefaultAlert.mm in Sources */,
|
||||
990F33B624BC915200D0F426 /* SearchActionBarView.swift in Sources */,
|
||||
99514BBA23E82B450085D3A7 /* ElevationProfileViewController.swift in Sources */,
|
||||
|
@ -5617,7 +5571,6 @@
|
|||
3444DFD21F17620C00E73099 /* MWMMapWidgetsHelper.mm in Sources */,
|
||||
348A8DFB1F66775A00D83026 /* RatingViewSettings.swift in Sources */,
|
||||
3472B5E1200F86C800DC6CD5 /* MWMEditorHelper.mm in Sources */,
|
||||
47B06DF021B697230094CCAD /* MWMGeoTrackerCore.mm in Sources */,
|
||||
99B6A74C2362F5AA002C94CB /* PromoButton.swift in Sources */,
|
||||
99F3EB1123F418C900C713F8 /* PlacePageBuilder.swift in Sources */,
|
||||
4735008A23A83CF700661A95 /* DownloadedMapsDataSource.swift in Sources */,
|
||||
|
@ -5738,7 +5691,6 @@
|
|||
F6E2FF4E1E097BA00083EBEC /* MWMAboutController.m in Sources */,
|
||||
993F5512237C622700545511 /* DeepLinkFileStrategy.swift in Sources */,
|
||||
47EF73FE247056A600D32AB8 /* GuidesGalleryCell.swift in Sources */,
|
||||
47B06DF921B95F5E0094CCAD /* IGeoTracker.swift in Sources */,
|
||||
CDCA27812243F59800167D87 /* CarPlayRouter.swift in Sources */,
|
||||
99CB34BD2369EAAC001D28AD /* TermsOfUsePresenter.swift in Sources */,
|
||||
34F5E0D41E3F254800B1C415 /* UIView+Hierarchy.swift in Sources */,
|
||||
|
@ -5905,7 +5857,6 @@
|
|||
3488B01A1E9D0B230068AFD8 /* UIColor+Modifications.swift in Sources */,
|
||||
6741AA281BF340DE002C974C /* MWMAlert.mm in Sources */,
|
||||
F6E2FF571E097BA00083EBEC /* MWMMobileInternetViewController.m in Sources */,
|
||||
47B06E0021BAAC270094CCAD /* GeoZoneTracker.swift in Sources */,
|
||||
99A906DD23F6F7030005872B /* AddReviewViewController.swift in Sources */,
|
||||
993DF11323F6BDB100AC231A /* UITableViewRenderer.swift in Sources */,
|
||||
3404F4952028A1B80090E401 /* BMCPermissionsCell.swift in Sources */,
|
||||
|
@ -5923,7 +5874,6 @@
|
|||
99A906E023F6F7030005872B /* RatingSummaryViewController.swift in Sources */,
|
||||
993F550D237C622700545511 /* DeepLinkLeadStrategy.swift in Sources */,
|
||||
337F98B221D3BAE600C8AC27 /* SearchCategoriesViewController.swift in Sources */,
|
||||
47B06DED21B696C20094CCAD /* GeoTracker.swift in Sources */,
|
||||
349FC54B1F680DAE00968C9F /* ExpandableReviewSettings.swift in Sources */,
|
||||
F6E2FE0A1E097BA00083EBEC /* MWMOpeningHoursDeleteScheduleTableViewCell.mm in Sources */,
|
||||
3454D7DA1E07F045004AF2AD /* UILabel+RuntimeAttributes.m in Sources */,
|
||||
|
@ -6070,9 +6020,7 @@
|
|||
DEVELOPMENT_TEAM = W55CJ2SCF7;
|
||||
"EXCLUDED_SOURCE_FILE_NAMES[sdk=iphonesimulator*]" = MetalContextFactory.mm;
|
||||
"EXCLUDED_SOURCE_FILE_NAMES[sdk=macosx*]" = MetalContextFactory.mm;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
);
|
||||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||
};
|
||||
|
@ -6089,9 +6037,7 @@
|
|||
DEVELOPMENT_TEAM = W55CJ2SCF7;
|
||||
"EXCLUDED_SOURCE_FILE_NAMES[sdk=iphonesimulator*]" = MetalContextFactory.mm;
|
||||
"EXCLUDED_SOURCE_FILE_NAMES[sdk=macosx*]" = MetalContextFactory.mm;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
);
|
||||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||
};
|
||||
|
@ -6119,9 +6065,7 @@
|
|||
"$(OMIM_ROOT)/3party/jansson/src",
|
||||
);
|
||||
INFOPLIST_FILE = OMaps.plist;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_CFLAGS = (
|
||||
"$(inherited)",
|
||||
"-Wall",
|
||||
|
|
|
@ -53,7 +53,6 @@ protocol PlacePageInfoViewControllerDelegate: AnyObject {
|
|||
func didPressCall()
|
||||
func didPressWebsite()
|
||||
func didPressEmail()
|
||||
func didPressLocalAd()
|
||||
}
|
||||
|
||||
class PlacePageInfoViewController: UIViewController {
|
||||
|
@ -78,7 +77,6 @@ class PlacePageInfoViewController: UIViewController {
|
|||
private var wifiView: InfoItemViewController?
|
||||
private var addressView: InfoItemViewController?
|
||||
private var coordinatesView: InfoItemViewController?
|
||||
private var localAdsButton: UIButton?
|
||||
|
||||
var placePageInfoData: PlacePageInfoData!
|
||||
weak var delegate: PlacePageInfoViewControllerDelegate?
|
||||
|
@ -158,35 +156,10 @@ class PlacePageInfoViewController: UIViewController {
|
|||
coordinatesView?.accessoryImage.image = UIImage(named: "ic_placepage_change")
|
||||
coordinatesView?.accessoryImage.isHidden = false
|
||||
coordinatesView?.canShowMenu = true
|
||||
|
||||
// switch placePageInfoData.localAdsStatus {
|
||||
// case .candidate:
|
||||
// localAdsButton = createLocalAdsButton(L("create_campaign_button"))
|
||||
// case .customer:
|
||||
// localAdsButton = createLocalAdsButton(L("view_campaign_button"))
|
||||
// case .notAvailable, .hidden:
|
||||
// coordinatesView?.separatorView.isHidden = true
|
||||
// @unknown default:
|
||||
// fatalError()
|
||||
// }
|
||||
}
|
||||
|
||||
// MARK: private
|
||||
|
||||
@objc private func onLocalAdsButton(_ sender: UIButton) {
|
||||
delegate?.didPressLocalAd()
|
||||
}
|
||||
|
||||
private func createLocalAdsButton(_ title: String) -> UIButton {
|
||||
let button = UIButton()
|
||||
button.setTitle(title, for: .normal)
|
||||
button.styleName = "FlatNormalTransButtonBig"
|
||||
button.heightAnchor.constraint(equalToConstant: 44).isActive = true
|
||||
stackView.addArrangedSubview(button)
|
||||
button.addTarget(self, action: #selector(onLocalAdsButton(_:)), for: .touchUpInside)
|
||||
return button
|
||||
}
|
||||
|
||||
private func createInfoItem(_ info: String,
|
||||
icon: UIImage?,
|
||||
style: Style = .regular,
|
||||
|
|
|
@ -52,10 +52,6 @@ extension PlacePageInteractor: PlacePageInfoViewControllerDelegate {
|
|||
func didPressEmail() {
|
||||
|
||||
}
|
||||
|
||||
func didPressLocalAd() {
|
||||
MWMPlacePageManagerHelper.openLocalAdsURL(placePageData)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WikiDescriptionViewControllerDelegate
|
||||
|
|
|
@ -444,23 +444,6 @@ void RegisterEventIfPossible(eye::MapObject::Event::Type const type)
|
|||
[[MapViewController sharedController].navigationController pushViewController:vc animated:YES];
|
||||
}
|
||||
|
||||
- (void)openLocalAdsURL:(PlacePageData *)data {
|
||||
NSURL *url = [NSURL URLWithString:data.infoData.localAdsUrl];
|
||||
if (!url)
|
||||
return;
|
||||
|
||||
auto const & feature = GetFramework().GetCurrentPlacePageInfo().GetID();
|
||||
[Statistics logEvent:kStatPlacePageOwnershipButtonClick
|
||||
withParameters:@{
|
||||
@"mwm_name" : @(feature.GetMwmName().c_str()),
|
||||
@"mwm_version" : @(feature.GetMwmVersion()),
|
||||
@"feature_id" : @(feature.m_index)
|
||||
}
|
||||
atLocation:[MWMLocationManager lastLocation]];
|
||||
|
||||
[self.ownerViewController openUrl:url];
|
||||
}
|
||||
|
||||
- (void)openWebsite:(PlacePageData *)data {
|
||||
NSURL *url = [NSURL URLWithString:data.infoData.website];
|
||||
if (url) {
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
+ (void)addPlace:(CLLocationCoordinate2D)coordinate;
|
||||
+ (void)orderTaxi:(PlacePageData *)data;
|
||||
+ (void)taxiShown:(PlacePageData *)data;
|
||||
+ (void)openLocalAdsURL:(PlacePageData *)data;
|
||||
+ (void)openWebsite:(PlacePageData *)data;
|
||||
+ (void)call:(PlacePageData *)data;
|
||||
+ (void)showAllFacilities:(PlacePageData *)data;
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
- (void)addPlace:(CLLocationCoordinate2D)coordinate;
|
||||
- (void)orderTaxi:(PlacePageData *)data;
|
||||
- (void)logTaxiShown:(PlacePageData *)data;
|
||||
- (void)openLocalAdsURL:(PlacePageData *)data;
|
||||
- (void)openWebsite:(PlacePageData *)data;
|
||||
- (void)call:(PlacePageData *)data;
|
||||
- (void)showAllFacilities:(PlacePageData *)data;
|
||||
|
@ -83,10 +82,6 @@
|
|||
[[MWMMapViewControlsManager manager].placePageManager logTaxiShown:data];
|
||||
}
|
||||
|
||||
+ (void)openLocalAdsURL:(PlacePageData *)data {
|
||||
[[MWMMapViewControlsManager manager].placePageManager openLocalAdsURL:data];
|
||||
}
|
||||
|
||||
+ (void)openWebsite:(PlacePageData *)data {
|
||||
[[MWMMapViewControlsManager manager].placePageManager openWebsite:data];
|
||||
}
|
||||
|
|
|
@ -125,9 +125,7 @@ bool PopularityHasHigherPriority(bool hasPosition, double distanceInMeters)
|
|||
self.popularView.hidden = !showPopular;
|
||||
}
|
||||
|
||||
if (productInfo.m_isLocalAdsCustomer)
|
||||
[self setStyleAndApply: @"SearchCellAds"];
|
||||
else if (isAvailable)
|
||||
if (isAvailable)
|
||||
[self setStyleAndApply: @"SearchCellAvaliable"];
|
||||
else
|
||||
[self setStyleAndApply: @"Background"];
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
project(local_ads)
|
||||
|
||||
include_directories(
|
||||
${OMIM_ROOT}/3party/jansson/src
|
||||
)
|
||||
|
||||
set(
|
||||
SRC
|
||||
campaign.hpp
|
||||
campaign_serialization.cpp
|
||||
campaign_serialization.hpp
|
||||
config.hpp
|
||||
event.cpp
|
||||
event.hpp
|
||||
file_helpers.cpp
|
||||
file_helpers.hpp
|
||||
icons_info.cpp
|
||||
icons_info.hpp
|
||||
statistics.cpp
|
||||
statistics.hpp
|
||||
)
|
||||
|
||||
omim_add_library(${PROJECT_NAME} ${SRC})
|
||||
|
||||
omim_add_pybindings_subdirectory(pylocal_ads)
|
||||
omim_add_test_subdirectory(local_ads_tests)
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "local_ads/icons_info.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace local_ads
|
||||
{
|
||||
struct Campaign
|
||||
{
|
||||
// Constructor for data Version::v1
|
||||
Campaign(uint32_t featureId, uint16_t iconId, uint8_t daysBeforeExpired)
|
||||
: m_featureId(featureId), m_iconId(iconId), m_daysBeforeExpired(daysBeforeExpired)
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor for data Version::v2
|
||||
Campaign(uint32_t featureId, uint16_t iconId, uint8_t daysBeforeExpired, uint8_t zoomLevel,
|
||||
uint8_t priority)
|
||||
: m_featureId(featureId)
|
||||
, m_iconId(iconId)
|
||||
, m_daysBeforeExpired(daysBeforeExpired)
|
||||
, m_minZoomLevel(zoomLevel)
|
||||
, m_priority(priority)
|
||||
{
|
||||
}
|
||||
|
||||
std::string GetIconName() const { return IconsInfo::Instance().GetIcon(m_iconId); }
|
||||
uint32_t m_featureId = 0;
|
||||
uint16_t m_iconId = 0;
|
||||
// Supported values range: 0-255. In case when accurate value is more than 255, we expect updated
|
||||
// data will be received during this time interval. For ex. accurate value is 365, in this case
|
||||
// first 110 days this field will store value 255.
|
||||
uint8_t m_daysBeforeExpired = 0;
|
||||
// Supported values range: 10-17.
|
||||
uint8_t m_minZoomLevel = 16;
|
||||
// Supported values range: 0-7
|
||||
uint8_t m_priority = 0;
|
||||
};
|
||||
|
||||
inline bool operator==(Campaign const & a, Campaign const & b)
|
||||
{
|
||||
return a.m_featureId == b.m_featureId &&
|
||||
a.m_iconId == b.m_iconId &&
|
||||
a.m_daysBeforeExpired == b.m_daysBeforeExpired &&
|
||||
a.m_minZoomLevel == b.m_minZoomLevel &&
|
||||
a.m_priority == b.m_priority;
|
||||
}
|
||||
} // namespace local_ads
|
|
@ -1,238 +0,0 @@
|
|||
#include "local_ads/campaign_serialization.hpp"
|
||||
|
||||
#include "coding/byte_stream.hpp"
|
||||
#include "coding/reader.hpp"
|
||||
#include "coding/varint.hpp"
|
||||
#include "coding/write_to_sink.hpp"
|
||||
|
||||
#include "base/exception.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/stl_helpers.hpp"
|
||||
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace local_ads;
|
||||
|
||||
DECLARE_EXCEPTION(UnknownVersion, RootException);
|
||||
|
||||
auto const kHalfByteShift = CHAR_BIT / 2;
|
||||
auto const kHalfByteMaxValue = 15;
|
||||
auto const kLowerMask = 0x0F;
|
||||
auto const kUpperMask = 0xF0;
|
||||
auto const kMinZoomLevel = 10;
|
||||
auto const kMaxZoomLevel = 17;
|
||||
auto const kMaxPriority = 7;
|
||||
|
||||
template <typename Integral, typename Source,
|
||||
std::enable_if_t<std::is_integral<Integral>::value, void *> = nullptr>
|
||||
std::vector<Integral> ReadVarUintArray(Source & s, size_t chunksNumber)
|
||||
{
|
||||
std::vector<Integral> result;
|
||||
for (size_t i = 0; i < chunksNumber; ++i)
|
||||
result.emplace_back(static_cast<Integral>(ReadVarUint<uint64_t>(s)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Integral, typename Source>
|
||||
std::vector<Integral> ReadArray(Source & s, size_t chunksNumber)
|
||||
{
|
||||
std::vector<Integral> result;
|
||||
for (size_t i = 0; i < chunksNumber; ++i)
|
||||
{
|
||||
result.emplace_back(ReadPrimitiveFromSource<Integral>(s));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SerializeV1(std::vector<Campaign> const & campaigns)
|
||||
{
|
||||
std::vector<uint8_t> buff;
|
||||
PushBackByteSink<decltype(buff)> dst(buff);
|
||||
WriteToSink(dst, Version::V1);
|
||||
WriteToSink(dst, campaigns.size());
|
||||
for (auto const & c : campaigns)
|
||||
WriteVarUint(dst, c.m_featureId);
|
||||
for (auto const & c : campaigns)
|
||||
WriteVarUint(dst, c.m_iconId);
|
||||
for (auto const & c : campaigns)
|
||||
WriteVarUint(dst, c.m_daysBeforeExpired);
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
std::vector<Campaign> DeserializeV1(std::vector<uint8_t> const & bytes)
|
||||
{
|
||||
ReaderSource<MemReaderWithExceptions> src({bytes.data(), bytes.size()});
|
||||
|
||||
CHECK_EQUAL(ReadPrimitiveFromSource<Version>(src), Version::V1, ());
|
||||
size_t const chunksNumber = static_cast<size_t>(ReadPrimitiveFromSource<uint64_t>(src));
|
||||
|
||||
auto const featureIds = ReadVarUintArray<uint32_t>(src, chunksNumber);
|
||||
auto const icons = ReadVarUintArray<uint16_t>(src, chunksNumber);
|
||||
auto const expirations = ReadVarUintArray<uint8_t>(src, chunksNumber);
|
||||
|
||||
std::vector<Campaign> campaigns;
|
||||
campaigns.reserve(chunksNumber);
|
||||
for (size_t i = 0; i < chunksNumber; ++i)
|
||||
{
|
||||
campaigns.emplace_back(featureIds[i], icons[i], expirations[i]);
|
||||
}
|
||||
return campaigns;
|
||||
}
|
||||
|
||||
uint8_t ZoomIndex(uint8_t zoomValue) { return zoomValue - kMinZoomLevel; }
|
||||
|
||||
uint8_t ZoomValue(uint8_t zoomIndex) { return zoomIndex + kMinZoomLevel; }
|
||||
|
||||
uint8_t PackZoomAndPriority(uint8_t minZoomLevel, uint8_t priority)
|
||||
{
|
||||
UNUSED_VALUE(kMaxZoomLevel);
|
||||
UNUSED_VALUE(kMaxPriority);
|
||||
UNUSED_VALUE(kHalfByteMaxValue);
|
||||
|
||||
ASSERT_GREATER_OR_EQUAL(minZoomLevel, kMinZoomLevel, ("Unsupported zoom level"));
|
||||
ASSERT_LESS_OR_EQUAL(minZoomLevel, kMaxZoomLevel, ("Unsupported zoom level"));
|
||||
ASSERT_LESS_OR_EQUAL(priority, kMaxPriority, ("Unsupported priority value"));
|
||||
|
||||
auto const zoomIndex = ZoomIndex(minZoomLevel);
|
||||
ASSERT_LESS_OR_EQUAL(zoomIndex, kHalfByteMaxValue, ());
|
||||
ASSERT_LESS_OR_EQUAL(priority, kHalfByteMaxValue, ());
|
||||
|
||||
// Pack zoom and priority into single byte.
|
||||
return (zoomIndex & kLowerMask) | ((priority << kHalfByteShift) & kUpperMask);
|
||||
}
|
||||
|
||||
uint8_t UnpackZoom(uint8_t src)
|
||||
{
|
||||
return ZoomValue(src & kLowerMask);
|
||||
}
|
||||
|
||||
uint8_t UnpackPriority(uint8_t src)
|
||||
{
|
||||
return (src >> kHalfByteShift) & kLowerMask;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SerializeV2(std::vector<Campaign> const & campaigns)
|
||||
{
|
||||
std::vector<uint8_t> buff;
|
||||
PushBackByteSink<decltype(buff)> dst(buff);
|
||||
WriteToSink(dst, Version::V2);
|
||||
WriteToSink(dst, campaigns.size());
|
||||
for (auto const & c : campaigns)
|
||||
WriteVarUint(dst, c.m_featureId);
|
||||
for (auto const & c : campaigns)
|
||||
WriteVarUint(dst, c.m_iconId);
|
||||
for (auto const & c : campaigns)
|
||||
WriteVarUint(dst, c.m_daysBeforeExpired);
|
||||
for (auto const & c : campaigns)
|
||||
WriteToSink(dst, PackZoomAndPriority(c.m_minZoomLevel, c.m_priority));
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
std::vector<Campaign> DeserializeV2(std::vector<uint8_t> const & bytes)
|
||||
{
|
||||
ReaderSource<MemReaderWithExceptions> src({bytes.data(), bytes.size()});
|
||||
|
||||
CHECK_EQUAL(ReadPrimitiveFromSource<Version>(src), Version::V2, ());
|
||||
size_t const chunksNumber = static_cast<size_t>(ReadPrimitiveFromSource<uint64_t>(src));
|
||||
|
||||
auto const featureIds = ReadVarUintArray<uint32_t>(src, chunksNumber);
|
||||
auto const icons = ReadVarUintArray<uint16_t>(src, chunksNumber);
|
||||
auto const expirations = ReadVarUintArray<uint8_t>(src, chunksNumber);
|
||||
|
||||
auto const zoomAndPriority = ReadArray<uint8_t>(src, chunksNumber);
|
||||
|
||||
std::vector<Campaign> campaigns;
|
||||
campaigns.reserve(chunksNumber);
|
||||
for (size_t i = 0; i < chunksNumber; ++i)
|
||||
{
|
||||
campaigns.emplace_back(featureIds[i], icons[i], expirations[i],
|
||||
UnpackZoom(zoomAndPriority[i]),
|
||||
UnpackPriority(zoomAndPriority[i]));
|
||||
|
||||
ASSERT_GREATER_OR_EQUAL(campaigns.back().m_minZoomLevel, kMinZoomLevel,
|
||||
("Unsupported zoom level"));
|
||||
ASSERT_LESS_OR_EQUAL(campaigns.back().m_minZoomLevel, kMaxZoomLevel,
|
||||
("Unsupported zoom level"));
|
||||
ASSERT_LESS_OR_EQUAL(campaigns.back().m_priority, kMaxPriority, ("Unsupported priority value"));
|
||||
}
|
||||
return campaigns;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace local_ads
|
||||
{
|
||||
std::vector<uint8_t> Serialize(std::vector<Campaign> const & campaigns, Version const version)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case Version::V1: return SerializeV1(campaigns);
|
||||
case Version::V2: return SerializeV2(campaigns);
|
||||
default: MYTHROW(UnknownVersion, (version));
|
||||
}
|
||||
}
|
||||
catch (RootException const & e)
|
||||
{
|
||||
LOG(LERROR, ("Cannot to serialize campaigns", e.what(), e.Msg()));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Serialize(std::vector<Campaign> const & campaigns)
|
||||
{
|
||||
return Serialize(campaigns, Version::Latest);
|
||||
}
|
||||
|
||||
std::vector<Campaign> Deserialize(std::vector<uint8_t> const & bytes)
|
||||
{
|
||||
try
|
||||
{
|
||||
ReaderSource<MemReaderWithExceptions> src({bytes.data(), bytes.size()});
|
||||
|
||||
auto const version = ReadPrimitiveFromSource<Version>(src);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case Version::V1: return DeserializeV1(bytes);
|
||||
case Version::V2: return DeserializeV2(bytes);
|
||||
default: MYTHROW(UnknownVersion, (version));
|
||||
}
|
||||
}
|
||||
catch (RootException const & e)
|
||||
{
|
||||
LOG(LERROR, ("Cannot to deserialize received data", e.what(), e.Msg()));
|
||||
}
|
||||
catch (std::bad_alloc const & e)
|
||||
{
|
||||
LOG(LERROR, ("Cannot to allocate memory for local ads campaigns", e.what()));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string DebugPrint(local_ads::Version version)
|
||||
{
|
||||
using local_ads::Version;
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case Version::Unknown: return "Unknown";
|
||||
case Version::V1: return "Version 1";
|
||||
case Version::V2: return "Version 2";
|
||||
default: ASSERT(false, ("Unknown version"));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
} // namespace local_ads
|
|
@ -1,26 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "local_ads/campaign.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace local_ads
|
||||
{
|
||||
enum class Version
|
||||
{
|
||||
Unknown = -1,
|
||||
// March 2017 (store feature ids and icon ids as varints, use one byte for days before
|
||||
// expiration).
|
||||
V1 = 0,
|
||||
// August 2017 (store zoom level and priority as 0-7 values in one byte).
|
||||
V2 = 1,
|
||||
|
||||
Latest = V2
|
||||
};
|
||||
std::vector<uint8_t> Serialize(std::vector<Campaign> const & campaigns, Version const version);
|
||||
std::vector<uint8_t> Serialize(std::vector<Campaign> const & campaigns);
|
||||
std::vector<Campaign> Deserialize(std::vector<uint8_t> const & bytes);
|
||||
|
||||
std::string DebugPrint(local_ads::Version version);
|
||||
} // namespace local_ads
|
|
@ -1,7 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
// Use local ads servers for development.
|
||||
//#define DEV_LOCAL_ADS_SERVER
|
||||
|
||||
// Use stage local ads servers.
|
||||
//#define STAGE_LOCAL_ADS_SERVER
|
|
@ -1,60 +0,0 @@
|
|||
#include "local_ads/event.hpp"
|
||||
|
||||
#include "base/math.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace local_ads
|
||||
{
|
||||
Event::Event(EventType type, int64_t mwmVersion, std::string const & countryId, uint32_t featureId,
|
||||
uint8_t zoomLevel, Timestamp const & timestamp, double latitude, double longitude,
|
||||
uint16_t accuracyInMeters)
|
||||
: m_type(type)
|
||||
, m_mwmVersion(mwmVersion)
|
||||
, m_countryId(countryId)
|
||||
, m_featureId(featureId)
|
||||
, m_zoomLevel(zoomLevel)
|
||||
, m_timestamp(timestamp)
|
||||
, m_latitude(latitude)
|
||||
, m_longitude(longitude)
|
||||
, m_accuracyInMeters(accuracyInMeters)
|
||||
{
|
||||
}
|
||||
|
||||
bool Event::operator<(Event const & event) const
|
||||
{
|
||||
if (m_mwmVersion != event.m_mwmVersion)
|
||||
return m_mwmVersion < event.m_mwmVersion;
|
||||
|
||||
if (m_countryId != event.m_countryId)
|
||||
return m_countryId < event.m_countryId;
|
||||
|
||||
return m_timestamp < event.m_timestamp;
|
||||
}
|
||||
|
||||
bool Event::operator==(Event const & event) const
|
||||
{
|
||||
double const kEps = 1e-5;
|
||||
using namespace std::chrono;
|
||||
return m_type == event.m_type && m_mwmVersion == event.m_mwmVersion &&
|
||||
m_countryId == event.m_countryId && m_featureId == event.m_featureId &&
|
||||
m_zoomLevel == event.m_zoomLevel &&
|
||||
base::AlmostEqualAbs(m_latitude, event.m_latitude, kEps) &&
|
||||
base::AlmostEqualAbs(m_longitude, event.m_longitude, kEps) &&
|
||||
m_accuracyInMeters == event.m_accuracyInMeters &&
|
||||
duration_cast<seconds>(m_timestamp - event.m_timestamp).count() == 0;
|
||||
}
|
||||
|
||||
std::string DebugPrint(Event const & event)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
std::ostringstream s;
|
||||
s << "[Type:" << static_cast<uint32_t>(event.m_type) << "; Country: " << event.m_countryId
|
||||
<< "; Version: " << event.m_mwmVersion << "; FID: " << event.m_featureId
|
||||
<< "; Zoom: " << static_cast<uint32_t>(event.m_zoomLevel)
|
||||
<< "; Ts: " << duration_cast<std::chrono::seconds>(event.m_timestamp.time_since_epoch()).count()
|
||||
<< "; LatLon: " << event.m_latitude << ", " << event.m_longitude
|
||||
<< "; Accuracy: " << event.m_accuracyInMeters << "]";
|
||||
return s.str();
|
||||
}
|
||||
} // namespace local_ads
|
|
@ -1,41 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
namespace local_ads
|
||||
{
|
||||
using Clock = std::chrono::system_clock;
|
||||
using Timestamp = Clock::time_point;
|
||||
|
||||
enum class EventType
|
||||
{
|
||||
ShowPoint = 0,
|
||||
OpenInfo,
|
||||
ClickedPhone,
|
||||
ClickedWebsite,
|
||||
Visit
|
||||
};
|
||||
|
||||
struct Event
|
||||
{
|
||||
EventType m_type;
|
||||
int64_t m_mwmVersion;
|
||||
std::string m_countryId;
|
||||
uint32_t m_featureId;
|
||||
uint8_t m_zoomLevel;
|
||||
Timestamp m_timestamp;
|
||||
double m_latitude;
|
||||
double m_longitude;
|
||||
uint16_t m_accuracyInMeters;
|
||||
|
||||
Event(EventType type, int64_t mwmVersion, std::string const & countryId, uint32_t featureId,
|
||||
uint8_t zoomLevel, Timestamp const & timestamp, double latitude, double longitude,
|
||||
uint16_t accuracyInMeters);
|
||||
|
||||
bool operator<(Event const & event) const;
|
||||
bool operator==(Event const & event) const;
|
||||
};
|
||||
|
||||
std::string DebugPrint(Event const & event);
|
||||
} // namespace local_ads
|
|
@ -1,54 +0,0 @@
|
|||
#include "local_ads/file_helpers.hpp"
|
||||
|
||||
#include "coding/reader.hpp"
|
||||
#include "coding/string_utf8_multilang.hpp"
|
||||
#include "coding/write_to_sink.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace local_ads
|
||||
{
|
||||
void WriteCountryName(FileWriter & writer, std::string const & countryName)
|
||||
{
|
||||
ASSERT(!countryName.empty(), ());
|
||||
utils::WriteString(writer, countryName);
|
||||
}
|
||||
|
||||
void WriteZigZag(FileWriter & writer, int64_t duration)
|
||||
{
|
||||
uint64_t const encoded = bits::ZigZagEncode(duration);
|
||||
WriteToSink(writer, encoded);
|
||||
}
|
||||
|
||||
void WriteRawData(FileWriter & writer, std::vector<uint8_t> const & rawData)
|
||||
{
|
||||
auto const size = static_cast<size_t>(rawData.size());
|
||||
WriteToSink(writer, size);
|
||||
writer.Write(rawData.data(), size);
|
||||
}
|
||||
|
||||
std::string ReadCountryName(ReaderSource<FileReader> & src)
|
||||
{
|
||||
std::string countryName;
|
||||
utils::ReadString<decltype(src), true>(src, countryName);
|
||||
return countryName;
|
||||
}
|
||||
|
||||
int64_t ReadZigZag(ReaderSource<FileReader> & src)
|
||||
{
|
||||
uint64_t const value = ReadPrimitiveFromSource<uint64_t>(src);
|
||||
return bits::ZigZagDecode(value);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ReadRawData(ReaderSource<FileReader> & src)
|
||||
{
|
||||
uint64_t const size = ReadPrimitiveFromSource<uint64_t>(src);
|
||||
if (static_cast<uint64_t>(src.Size()) < size)
|
||||
MYTHROW(Reader::SizeException, (src.Pos(), size));
|
||||
std::vector<uint8_t> bytes(static_cast<size_t>(size));
|
||||
src.Read(bytes.data(), bytes.size());
|
||||
return bytes;
|
||||
}
|
||||
} // namespace local_ads
|
|
@ -1,40 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "local_ads/event.hpp"
|
||||
|
||||
#include "coding/file_reader.hpp"
|
||||
#include "coding/file_writer.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace local_ads
|
||||
{
|
||||
void WriteCountryName(FileWriter & writer, std::string const & countryName);
|
||||
|
||||
void WriteZigZag(FileWriter & writer, int64_t duration);
|
||||
|
||||
template <typename Duration>
|
||||
void WriteTimestamp(FileWriter & writer, Timestamp ts)
|
||||
{
|
||||
int64_t const d = std::chrono::duration_cast<Duration>(ts.time_since_epoch()).count();
|
||||
WriteZigZag(writer, d);
|
||||
}
|
||||
|
||||
void WriteRawData(FileWriter & writer, std::vector<uint8_t> const & rawData);
|
||||
|
||||
std::string ReadCountryName(ReaderSource<FileReader> & src);
|
||||
|
||||
int64_t ReadZigZag(ReaderSource<FileReader> & src);
|
||||
|
||||
template <typename Duration>
|
||||
Timestamp ReadTimestamp(ReaderSource<FileReader> & src)
|
||||
{
|
||||
int64_t const d = ReadZigZag(src);
|
||||
return Timestamp(Duration(d));
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ReadRawData(ReaderSource<FileReader> & src);
|
||||
} // namespace local_ads
|
|
@ -1,73 +0,0 @@
|
|||
#include "local_ads/icons_info.hpp"
|
||||
|
||||
#include "coding/reader.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace
|
||||
{
|
||||
char const kDelimiter = '_';
|
||||
|
||||
void ParseIconsFile(std::string const & iconsFile,
|
||||
std::function<void(std::string const &)> const & handler)
|
||||
{
|
||||
ASSERT(handler != nullptr, ());
|
||||
try
|
||||
{
|
||||
std::string fileData;
|
||||
ReaderPtr<Reader>(GetPlatform().GetReader(iconsFile)).ReadAsString(fileData);
|
||||
strings::Tokenize(fileData, "\n", [&handler](std::string const & str) {
|
||||
if (!str.empty())
|
||||
handler(str);
|
||||
});
|
||||
}
|
||||
catch (Reader::Exception const & e)
|
||||
{
|
||||
LOG(LWARNING, ("Error reading file ", iconsFile, " : ", e.what()));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace local_ads
|
||||
{
|
||||
IconsInfo & IconsInfo::Instance()
|
||||
{
|
||||
static IconsInfo iconsInfo;
|
||||
return iconsInfo;
|
||||
}
|
||||
|
||||
void IconsInfo::SetSourceFile(std::string const & fileName)
|
||||
{
|
||||
std::map<uint16_t, std::string> icons;
|
||||
ParseIconsFile(fileName, [&icons](std::string const & icon) {
|
||||
auto const pos = icon.find(kDelimiter);
|
||||
if (pos == std::string::npos)
|
||||
return;
|
||||
uint32_t index;
|
||||
if (!strings::to_uint(icon.substr(0, pos), index))
|
||||
index = 0;
|
||||
icons[static_cast<uint16_t>(index)] = icon;
|
||||
});
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_fileName = fileName;
|
||||
std::swap(m_icons, icons);
|
||||
}
|
||||
}
|
||||
|
||||
std::string IconsInfo::GetIcon(uint16_t index) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
auto const it = m_icons.find(index);
|
||||
if (it == m_icons.end())
|
||||
return {};
|
||||
return it->second;
|
||||
}
|
||||
} // namespace local_ads
|
|
@ -1,30 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace local_ads
|
||||
{
|
||||
class IconsInfo
|
||||
{
|
||||
public:
|
||||
static IconsInfo & Instance();
|
||||
|
||||
void SetSourceFile(std::string const & fileName);
|
||||
std::string GetIcon(uint16_t index) const;
|
||||
|
||||
private:
|
||||
IconsInfo() = default;
|
||||
~IconsInfo() {}
|
||||
|
||||
std::string m_fileName;
|
||||
std::map<uint16_t, std::string> m_icons;
|
||||
mutable std::mutex m_mutex;
|
||||
|
||||
DISALLOW_COPY_AND_MOVE(IconsInfo);
|
||||
};
|
||||
} // namespace local_ads
|
|
@ -1,26 +0,0 @@
|
|||
project(local_ads_tests)
|
||||
|
||||
set(
|
||||
SRC
|
||||
campaign_serialization_test.cpp
|
||||
file_helpers_tests.cpp
|
||||
statistics_tests.cpp
|
||||
)
|
||||
|
||||
omim_add_test(${PROJECT_NAME} ${SRC})
|
||||
|
||||
omim_link_libraries(
|
||||
${PROJECT_NAME}
|
||||
local_ads
|
||||
platform_tests_support
|
||||
platform
|
||||
coding
|
||||
geometry
|
||||
base
|
||||
jansson
|
||||
oauthcpp
|
||||
stats_client
|
||||
${LIBZ}
|
||||
)
|
||||
|
||||
link_qt5_core(${PROJECT_NAME})
|
|
@ -1,94 +0,0 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "local_ads/campaign_serialization.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
using namespace local_ads;
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
using Limits = typename std::numeric_limits<T>;
|
||||
|
||||
bool TestSerialization(std::vector<Campaign> const & cs, Version const v)
|
||||
{
|
||||
auto const bytes = Serialize(cs, v);
|
||||
return cs == Deserialize(bytes);
|
||||
}
|
||||
|
||||
std::vector<Campaign> GenerateCampaignsV1(size_t number)
|
||||
{
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<uint32_t> featureIds(1, Limits<uint32_t>::max());
|
||||
std::uniform_int_distribution<> icons(1, Limits<uint16_t>::max());
|
||||
std::uniform_int_distribution<> expirationDays(1, Limits<uint8_t>::max());
|
||||
|
||||
std::vector<Campaign> cs;
|
||||
while (number--)
|
||||
{
|
||||
auto const fid = featureIds(gen);
|
||||
auto const iconid = icons(gen);
|
||||
auto const days = expirationDays(gen);
|
||||
cs.emplace_back(fid, iconid, days);
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
|
||||
std::vector<Campaign> GenerateCampaignsV2(size_t number)
|
||||
{
|
||||
int kSeed = 42;
|
||||
std::mt19937 gen(kSeed);
|
||||
std::uniform_int_distribution<uint32_t> featureIds(1, Limits<uint32_t>::max());
|
||||
std::uniform_int_distribution<> icons(1, Limits<uint16_t>::max());
|
||||
std::uniform_int_distribution<> expirationDays(1, Limits<uint8_t>::max());
|
||||
std::uniform_int_distribution<> zoomLevels(10, 17);
|
||||
std::uniform_int_distribution<> priorities(0, 7);
|
||||
|
||||
std::vector<Campaign> cs;
|
||||
while (number--)
|
||||
{
|
||||
auto const fid = featureIds(gen);
|
||||
auto const iconid = icons(gen);
|
||||
auto const days = expirationDays(gen);
|
||||
auto const zoom = zoomLevels(gen);
|
||||
auto const priority = priorities(gen);
|
||||
cs.emplace_back(fid, iconid, days, zoom, priority);
|
||||
}
|
||||
return cs;
|
||||
}
|
||||
} // namspace
|
||||
|
||||
UNIT_TEST(Serialization_Smoke)
|
||||
{
|
||||
TEST(TestSerialization({
|
||||
{0, 0, 0},
|
||||
{Limits<uint32_t>::max(), Limits<uint16_t>::max(), Limits<uint8_t>::max()},
|
||||
{120003, 456, 15}
|
||||
}, Version::V1), ());
|
||||
|
||||
TEST(TestSerialization({
|
||||
{0, 0, 0, 10, 0},
|
||||
{Limits<uint32_t>::max(), Limits<uint16_t>::max(), Limits<uint8_t>::max()},
|
||||
{1000, 100, 255, 17, 7},
|
||||
{120003, 456, 15, 13, 6}
|
||||
}, Version::V2), ());
|
||||
|
||||
TEST(TestSerialization({
|
||||
{241925, 6022, 255, 16, 6},
|
||||
{241927, 6036, 255, 16, 0},
|
||||
{164169, 3004, 255, 15, 0},
|
||||
{164172, 3001, 255, 15, 7}
|
||||
}, Version::V2), ());
|
||||
|
||||
TEST(TestSerialization(GenerateCampaignsV1(100), Version::V1), ());
|
||||
TEST(TestSerialization(GenerateCampaignsV1(1000), Version::V1), ());
|
||||
TEST(TestSerialization(GenerateCampaignsV1(10000), Version::V1), ());
|
||||
|
||||
TEST(TestSerialization(GenerateCampaignsV2(100), Version::V2), ());
|
||||
TEST(TestSerialization(GenerateCampaignsV2(1000), Version::V2), ());
|
||||
TEST(TestSerialization(GenerateCampaignsV2(10000), Version::V2), ());
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "local_ads/file_helpers.hpp"
|
||||
|
||||
#include "base/file_name_utils.hpp"
|
||||
|
||||
#include "platform/platform_tests_support/scoped_file.hpp"
|
||||
|
||||
using namespace local_ads;
|
||||
using namespace std;
|
||||
using platform::tests_support::ScopedFile;
|
||||
|
||||
UNIT_TEST(LocalAdsHelpers_Read_Write_Country_Name)
|
||||
{
|
||||
ScopedFile testFile("la_tests.dat", ScopedFile::Mode::Create);
|
||||
|
||||
string const countryName = "Russia_Moscow";
|
||||
{
|
||||
FileWriter writer(testFile.GetFullPath());
|
||||
WriteCountryName(writer, countryName);
|
||||
}
|
||||
|
||||
string result;
|
||||
{
|
||||
FileReader reader(testFile.GetFullPath());
|
||||
ReaderSource<FileReader> src(reader);
|
||||
result = ReadCountryName(src);
|
||||
}
|
||||
|
||||
TEST_EQUAL(result, countryName, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(LocalAdsHelpers_Read_Write_Timestamp)
|
||||
{
|
||||
ScopedFile testFile("la_tests.dat", ScopedFile::Mode::Create);
|
||||
|
||||
auto ts = local_ads::Clock::now();
|
||||
{
|
||||
FileWriter writer(testFile.GetFullPath());
|
||||
WriteTimestamp<chrono::hours>(writer, ts);
|
||||
WriteTimestamp<chrono::seconds>(writer, ts);
|
||||
}
|
||||
|
||||
local_ads::Timestamp resultInHours;
|
||||
local_ads::Timestamp resultInSeconds;
|
||||
{
|
||||
FileReader reader(testFile.GetFullPath());
|
||||
ReaderSource<FileReader> src(reader);
|
||||
resultInHours = ReadTimestamp<chrono::hours>(src);
|
||||
resultInSeconds = ReadTimestamp<chrono::seconds>(src);
|
||||
}
|
||||
|
||||
TEST_EQUAL(chrono::duration_cast<chrono::hours>(ts - resultInHours).count(), 0, ());
|
||||
TEST_EQUAL(chrono::duration_cast<chrono::seconds>(ts - resultInSeconds).count(), 0, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(LocalAdsHelpers_Read_Write_RawData)
|
||||
{
|
||||
ScopedFile testFile("la_tests.dat", ScopedFile::Mode::Create);
|
||||
|
||||
vector<uint8_t> rawData = {1, 2, 3, 4, 5};
|
||||
{
|
||||
FileWriter writer(testFile.GetFullPath());
|
||||
WriteRawData(writer, rawData);
|
||||
}
|
||||
|
||||
vector<uint8_t> result;
|
||||
{
|
||||
FileReader reader(testFile.GetFullPath());
|
||||
ReaderSource<FileReader> src(reader);
|
||||
result = ReadRawData(src);
|
||||
}
|
||||
|
||||
TEST_EQUAL(rawData, result, ());
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "local_ads/statistics.hpp"
|
||||
|
||||
#include "base/file_name_utils.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
class StatisticsGuard
|
||||
{
|
||||
public:
|
||||
explicit StatisticsGuard(local_ads::Statistics & statistics) : m_statistics(statistics) {}
|
||||
|
||||
~StatisticsGuard()
|
||||
{
|
||||
m_statistics.CleanupAfterTesting();
|
||||
}
|
||||
|
||||
private:
|
||||
local_ads::Statistics & m_statistics;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
using namespace std::chrono;
|
||||
using ET = local_ads::EventType;
|
||||
using TS = local_ads::Timestamp;
|
||||
|
||||
UNIT_TEST(LocalAdsStatistics_Read_Write_Simple)
|
||||
{
|
||||
local_ads::Statistics statistics;
|
||||
StatisticsGuard guard(statistics);
|
||||
|
||||
std::list<local_ads::Event> events;
|
||||
// type, mwmVersion, countryId, featureId, zoomLevel, timestamp, latitude, longitude, accuracyInMeters
|
||||
events.emplace_back(ET::ShowPoint, 123456, "Moscow", 111, 15, TS(minutes(5)), 30.0, 64.0, 10);
|
||||
events.emplace_back(ET::ShowPoint, 123456, "Moscow", 222, 13, TS(minutes(10)), 20.0, 14.0, 20);
|
||||
events.emplace_back(ET::OpenInfo, 123456, "Moscow", 111, 17, TS(minutes(15)), 53.0, 54.0, 10000);
|
||||
std::string unusedFileName;
|
||||
auto unprocessedEvents = statistics.WriteEventsForTesting(events, unusedFileName);
|
||||
TEST_EQUAL(unprocessedEvents.size(), 0, ());
|
||||
|
||||
TEST_EQUAL(statistics.ReadEventsForTesting("Moscow_123456.dat"), events, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(LocalAdsStatistics_Write_With_Unprocessed)
|
||||
{
|
||||
local_ads::Statistics statistics;
|
||||
StatisticsGuard guard(statistics);
|
||||
|
||||
std::list<local_ads::Event> events;
|
||||
events.emplace_back(ET::ShowPoint, 123456, "Moscow", 111, 15, TS(minutes(5)), 0.0, 0.0, 10);
|
||||
events.emplace_back(ET::ShowPoint, 123456, "Moscow", 222, 13, TS(minutes(10)), 20.0, 14.0, 20);
|
||||
events.emplace_back(ET::OpenInfo, 123456, "Moscow", 111, 17, TS(minutes(15)), 15.0, 14.0, 20);
|
||||
std::string fileNameToRebuild;
|
||||
auto unprocessedEvents = statistics.WriteEventsForTesting(events, fileNameToRebuild);
|
||||
TEST_EQUAL(unprocessedEvents.size(), 0, ());
|
||||
TEST(fileNameToRebuild.empty(), ());
|
||||
|
||||
std::list<local_ads::Event> events2;
|
||||
events2.emplace_back(ET::ShowPoint, 123456, "Moscow", 333, 15, TS(minutes(1)), 1.0, 89.0, 20);
|
||||
events2.emplace_back(ET::ShowPoint, 123456, "Moscow", 444, 15, TS(minutes(20)), 30.0, 13.0, 15);
|
||||
auto unprocessedEvents2 = statistics.WriteEventsForTesting(events2, fileNameToRebuild);
|
||||
|
||||
std::list<local_ads::Event> expectedUnprocessedEvents = events2;
|
||||
expectedUnprocessedEvents.sort();
|
||||
|
||||
base::GetNameFromFullPath(fileNameToRebuild);
|
||||
TEST_EQUAL(fileNameToRebuild, "Moscow_123456.dat", ());
|
||||
TEST_EQUAL(expectedUnprocessedEvents, unprocessedEvents2, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(LocalAdsStatistics_Process_With_Rebuild)
|
||||
{
|
||||
local_ads::Statistics statistics;
|
||||
StatisticsGuard guard(statistics);
|
||||
|
||||
std::list<local_ads::Event> events;
|
||||
events.emplace_back(ET::ShowPoint, 123456, "Moscow", 111, 15, TS(minutes(5)), 50.0, 14.0, 20);
|
||||
events.emplace_back(ET::ShowPoint, 123456, "Moscow", 222, 13, TS(minutes(10)), 69.0, 67.0, 100);
|
||||
events.emplace_back(ET::OpenInfo, 123456, "Moscow", 111, 17, TS(minutes(15)), 45.0, 80.0, 34);
|
||||
std::string unused;
|
||||
statistics.WriteEventsForTesting(events, unused);
|
||||
|
||||
TEST_EQUAL(statistics.ReadEventsForTesting("Moscow_123456.dat"), events, ());
|
||||
|
||||
std::list<local_ads::Event> events2;
|
||||
events2.emplace_back(ET::ShowPoint, 123456, "Moscow", 333, 15, TS(minutes(1)), 20.0, 14.0, 12);
|
||||
events2.emplace_back(ET::ShowPoint, 123456, "Moscow", 444, 15, TS(minutes(20)), 30.0, 56.0, 3535);
|
||||
|
||||
statistics.ProcessEventsForTesting(events2);
|
||||
|
||||
std::list<local_ads::Event> expectedResult = events;
|
||||
expectedResult.insert(expectedResult.end(), events2.begin(), events2.end());
|
||||
expectedResult.sort();
|
||||
|
||||
TEST_EQUAL(statistics.ReadEventsForTesting("Moscow_123456.dat"), expectedResult, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(LocalAdsStatistics_Process_With_Clipping)
|
||||
{
|
||||
local_ads::Statistics statistics;
|
||||
StatisticsGuard guard(statistics);
|
||||
|
||||
std::list<local_ads::Event> events;
|
||||
events.emplace_back(ET::ShowPoint, 123456, "Moscow", 111, 15, TS(minutes(5)), 20.0, 14.0, 12);
|
||||
events.emplace_back(ET::ShowPoint, 123456, "Moscow", 222, 13, TS(minutes(10)), 69.0, 67.0, 100);
|
||||
events.emplace_back(ET::OpenInfo, 123456, "Moscow", 111, 17, TS(minutes(25 * 60 + 15)), 20.0,
|
||||
14.0, 20);
|
||||
std::string unused;
|
||||
statistics.WriteEventsForTesting(events, unused);
|
||||
|
||||
TEST_EQUAL(statistics.ReadEventsForTesting("Moscow_123456.dat"), events, ());
|
||||
|
||||
std::list<local_ads::Event> events2;
|
||||
events2.emplace_back(ET::ShowPoint, 123456, "Moscow", 333, 15, TS(minutes(24 * 183 * 60 + 50)),
|
||||
20.0, 14.0, 20);
|
||||
|
||||
statistics.ProcessEventsForTesting(events2);
|
||||
|
||||
std::list<local_ads::Event> expectedResult;
|
||||
expectedResult.push_back(local_ads::Event(events.back()));
|
||||
expectedResult.insert(expectedResult.end(), events2.begin(), events2.end());
|
||||
expectedResult.sort();
|
||||
|
||||
TEST_EQUAL(statistics.ReadEventsForTesting("Moscow_123456.dat"), expectedResult, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(LocalAdsStatistics_Process_Complex)
|
||||
{
|
||||
local_ads::Statistics statistics;
|
||||
StatisticsGuard guard(statistics);
|
||||
|
||||
std::list<local_ads::Event> events;
|
||||
events.emplace_back(ET::ShowPoint, 123456, "Moscow", 111, 15, TS(minutes(5)), 20.0, 14.0, 20);
|
||||
events.emplace_back(ET::ShowPoint, 123456, "Minsk", 222, 13, TS(minutes(10)), 30.0, 14.0, 20);
|
||||
events.emplace_back(ET::OpenInfo, 123456, "Minsk", 111, 17, TS(minutes(25)), 40.0, 14.0, 20);
|
||||
events.emplace_back(ET::OpenInfo, 123456, "Minsk", 111, 17, TS(minutes(25 * 60 + 15)), 20.0, 14.0,
|
||||
20);
|
||||
std::string unused;
|
||||
statistics.WriteEventsForTesting(events, unused);
|
||||
|
||||
std::list<local_ads::Event> expectedResult1;
|
||||
expectedResult1.push_back(local_ads::Event(events.front()));
|
||||
TEST_EQUAL(statistics.ReadEventsForTesting("Moscow_123456.dat"), expectedResult1, ());
|
||||
|
||||
std::list<local_ads::Event> expectedResult2 = events;
|
||||
expectedResult2.erase(expectedResult2.begin());
|
||||
TEST_EQUAL(statistics.ReadEventsForTesting("Minsk_123456.dat"), expectedResult2, ());
|
||||
|
||||
std::list<local_ads::Event> events2;
|
||||
events2.emplace_back(ET::ShowPoint, 123456, "Moscow", 333, 15, TS(minutes(100)), 20.0, 14.0, 20);
|
||||
events2.emplace_back(ET::ShowPoint, 123456, "Minsk", 333, 15, TS(minutes(24 * 183 * 60 + 50)),
|
||||
20.0, 14.0, 20);
|
||||
|
||||
statistics.ProcessEventsForTesting(events2);
|
||||
|
||||
expectedResult1.push_back(local_ads::Event(events2.front()));
|
||||
TEST_EQUAL(statistics.ReadEventsForTesting("Moscow_123456.dat"), expectedResult1, ());
|
||||
|
||||
expectedResult2.clear();
|
||||
expectedResult2.push_back(local_ads::Event(events.back()));
|
||||
expectedResult2.push_back(local_ads::Event(events2.back()));
|
||||
TEST_EQUAL(statistics.ReadEventsForTesting("Minsk_123456.dat"), expectedResult2, ());
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
project(pylocal_ads)
|
||||
|
||||
set(
|
||||
SRC
|
||||
bindings.cpp
|
||||
)
|
||||
|
||||
include_directories(${CMAKE_BINARY_DIR})
|
||||
|
||||
omim_add_library(${PROJECT_NAME} MODULE ${SRC})
|
||||
|
||||
omim_link_libraries(
|
||||
${PROJECT_NAME}
|
||||
${Boost_LIBRARIES}
|
||||
local_ads
|
||||
base
|
||||
)
|
||||
|
||||
if (PLATFORM_MAC)
|
||||
omim_link_libraries(${PROJECT_NAME} "-Wl,-undefined,dynamic_lookup")
|
||||
endif()
|
||||
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
|
|
@ -1,77 +0,0 @@
|
|||
#include "local_ads/campaign.hpp"
|
||||
#include "local_ads/campaign_serialization.hpp"
|
||||
|
||||
// This header should be included due to a python compilation error.
|
||||
// pyport.h overwrites defined macros and replaces it with its own.
|
||||
// However, in the OS X c++ libraries, these are not macros but functions,
|
||||
// hence the error. See https://bugs.python.org/issue10910
|
||||
#include <locale>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wreorder"
|
||||
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-local-typedef"
|
||||
#endif
|
||||
|
||||
#include "pyhelpers/module_version.hpp"
|
||||
#include "pyhelpers/vector_uint8.hpp"
|
||||
#include "pyhelpers/vector_list_conversion.hpp"
|
||||
|
||||
#include <boost/python.hpp>
|
||||
#include <boost/python/enum.hpp>
|
||||
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
using namespace local_ads;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::vector<uint8_t> PySerialize(boost::python::list const & cs, Version const version)
|
||||
{
|
||||
auto const campaigns = pyhelpers::PythonListToStdVector<Campaign>(cs);
|
||||
return Serialize(campaigns, version);
|
||||
}
|
||||
|
||||
boost::python::list PyDeserialize(std::vector<uint8_t> const & blob)
|
||||
{
|
||||
auto const campaigns = Deserialize(blob);
|
||||
return pyhelpers::StdVectorToPythonList(campaigns);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
BOOST_PYTHON_MODULE(pylocal_ads)
|
||||
{
|
||||
using namespace boost::python;
|
||||
scope().attr("__version__") = PYBINDINGS_VERSION;
|
||||
|
||||
// Register the to-python converters.
|
||||
to_python_converter<std::vector<uint8_t>, vector_uint8t_to_str>();
|
||||
vector_uint8t_from_python_str();
|
||||
|
||||
class_<Campaign>("Campaign", init<uint32_t, uint16_t, uint8_t, uint8_t, uint8_t>())
|
||||
.def(init<uint32_t, uint16_t, uint8_t>())
|
||||
.def_readonly("m_featureId", &Campaign::m_featureId)
|
||||
.def_readonly("m_iconId", &Campaign::m_iconId)
|
||||
.def_readonly("m_daysBeforeExpired", &Campaign::m_daysBeforeExpired)
|
||||
.def_readonly("m_minZoomLevel", &Campaign::m_minZoomLevel)
|
||||
.def_readonly("m_priority", &Campaign::m_priority);
|
||||
|
||||
class_<std::vector<Campaign>>("CampaignList")
|
||||
.def(vector_indexing_suite<std::vector<Campaign>>());
|
||||
|
||||
enum_<Version>("Version")
|
||||
.value("UNKNOWN", Version::Unknown)
|
||||
.value("V1", Version::V1)
|
||||
.value("V2", Version::V2)
|
||||
.value("LATEST", Version::Latest)
|
||||
.export_values();
|
||||
|
||||
def("serialize", PySerialize);
|
||||
def("deserialize", PyDeserialize);
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
import unittest
|
||||
|
||||
from pylocal_ads import (Campaign, Version, serialize, deserialize)
|
||||
|
||||
|
||||
class PyLocalAdsTest(unittest.TestCase):
|
||||
|
||||
def assert_equal_campaigns(self, lhs, rhs):
|
||||
self.assertEqual(len(lhs), len(rhs))
|
||||
|
||||
for i in range(0, len(lhs)):
|
||||
self.assertEqual(lhs[i].m_featureId, rhs[i].m_featureId)
|
||||
self.assertEqual(lhs[i].m_iconId, rhs[i].m_iconId)
|
||||
self.assertEqual(lhs[i].m_daysBeforeExpired, rhs[i].m_daysBeforeExpired)
|
||||
self.assertEqual(lhs[i].m_minZoomLevel, rhs[i].m_minZoomLevel)
|
||||
self.assertEqual(lhs[i].m_priority, rhs[i].m_priority)
|
||||
|
||||
def test_smoke(self):
|
||||
campaigns_v1 = [
|
||||
Campaign(10, 10, 10),
|
||||
Campaign(1000, 100, 20),
|
||||
Campaign(120003, 456, 15)
|
||||
]
|
||||
|
||||
campaigns_v2 = [
|
||||
Campaign(10, 10, 10, 10, 0),
|
||||
Campaign(1000, 100, 20, 17, 7),
|
||||
Campaign(120003, 456, 15, 13, 6)
|
||||
]
|
||||
|
||||
serialized = serialize(campaigns_v1, Version.V1)
|
||||
result_v1 = deserialize(serialized)
|
||||
|
||||
self.assert_equal_campaigns(campaigns_v1, result_v1)
|
||||
|
||||
serialized = serialize(campaigns_v2, Version.V2)
|
||||
result_v2 = deserialize(serialized)
|
||||
|
||||
self.assert_equal_campaigns(campaigns_v2, result_v2)
|
||||
|
||||
serialized = serialize(campaigns_v2, Version.LATEST)
|
||||
result_latest = deserialize(serialized)
|
||||
|
||||
self.assert_equal_campaigns(campaigns_v2, result_latest)
|
||||
self.assert_equal_campaigns(result_v2, result_latest)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
|
@ -1,13 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
module_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
sys.path.insert(0, os.path.join(module_dir, '..', '..'))
|
||||
|
||||
from pyhelpers.setup import setup_omim_pybinding
|
||||
|
||||
|
||||
NAME = "pylocal_ads"
|
||||
|
||||
setup_omim_pybinding(name=NAME)
|
|
@ -1,584 +0,0 @@
|
|||
#include "local_ads/statistics.hpp"
|
||||
|
||||
#include "local_ads/config.hpp"
|
||||
#include "local_ads/file_helpers.hpp"
|
||||
|
||||
#include "platform/http_client.hpp"
|
||||
#include "platform/network_policy.hpp"
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "coding/file_writer.hpp"
|
||||
#include "coding/point_coding.hpp"
|
||||
#include "coding/url.hpp"
|
||||
#include "coding/write_to_sink.hpp"
|
||||
#include "coding/zlib.hpp"
|
||||
|
||||
#include "geometry/mercator.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/exception.hpp"
|
||||
#include "base/file_name_utils.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include "private.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include "3party/Alohalytics/src/alohalytics.h"
|
||||
#include "3party/jansson/myjansson.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string const kStatisticsFolderName = "local_ads_stats";
|
||||
std::string const kStatisticsExt = ".dat";
|
||||
|
||||
uint64_t constexpr kMaxFilesSizeInBytes = 10 * 1024 * 1024;
|
||||
float const kEventsDisposingRate = 0.2f;
|
||||
|
||||
auto constexpr kSendingTimeout = std::chrono::hours(1);
|
||||
int64_t constexpr kEventMaxLifetimeInSeconds = 24 * 183 * 3600; // About half of year.
|
||||
auto constexpr kDeletionPeriod = std::chrono::hours(24);
|
||||
|
||||
std::string const kStatisticsServer = LOCAL_ADS_STATISTICS_SERVER_URL;
|
||||
|
||||
void WriteMetadata(FileWriter & writer, std::string const & countryId, int64_t mwmVersion,
|
||||
local_ads::Timestamp const & ts)
|
||||
{
|
||||
local_ads::WriteCountryName(writer, countryId);
|
||||
local_ads::WriteZigZag(writer, mwmVersion);
|
||||
local_ads::WriteTimestamp<std::chrono::seconds>(writer, ts);
|
||||
}
|
||||
|
||||
void ReadMetadata(ReaderSource<FileReader> & src, std::string & countryId, int64_t & mwmVersion,
|
||||
local_ads::Timestamp & ts)
|
||||
{
|
||||
countryId = local_ads::ReadCountryName(src);
|
||||
mwmVersion = local_ads::ReadZigZag(src);
|
||||
ts = local_ads::ReadTimestamp<std::chrono::seconds>(src);
|
||||
}
|
||||
|
||||
void WritePackedData(FileWriter & writer, local_ads::Statistics::PackedData && packedData)
|
||||
{
|
||||
WriteToSink(writer, packedData.m_eventType);
|
||||
WriteToSink(writer, packedData.m_zoomLevel);
|
||||
WriteToSink(writer, packedData.m_featureIndex);
|
||||
WriteToSink(writer, packedData.m_seconds);
|
||||
local_ads::WriteZigZag(writer, packedData.m_mercator);
|
||||
WriteToSink(writer, packedData.m_accuracy);
|
||||
}
|
||||
|
||||
template <typename ToDo>
|
||||
void ReadPackedData(ReaderSource<FileReader> & src, ToDo && toDo)
|
||||
{
|
||||
using PackedData = local_ads::Statistics::PackedData;
|
||||
|
||||
std::string countryId;
|
||||
int64_t mwmVersion;
|
||||
local_ads::Timestamp baseTimestamp;
|
||||
ReadMetadata(src, countryId, mwmVersion, baseTimestamp);
|
||||
while (src.Size() > 0)
|
||||
{
|
||||
PackedData data;
|
||||
data.m_eventType = ReadPrimitiveFromSource<uint8_t>(src);
|
||||
data.m_zoomLevel = ReadPrimitiveFromSource<uint8_t>(src);
|
||||
data.m_featureIndex = ReadPrimitiveFromSource<uint32_t>(src);
|
||||
data.m_seconds = ReadPrimitiveFromSource<uint32_t>(src);
|
||||
data.m_mercator = local_ads::ReadZigZag(src);
|
||||
data.m_accuracy = ReadPrimitiveFromSource<uint16_t>(src);
|
||||
toDo(std::move(data), countryId, mwmVersion, baseTimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ToDo>
|
||||
void FilterEvents(std::list<local_ads::Event> const & events, std::string const & countryId,
|
||||
int64_t mwmVersion, ToDo && toDo)
|
||||
{
|
||||
for (auto const & event : events)
|
||||
{
|
||||
if (event.m_countryId != countryId || event.m_mwmVersion != mwmVersion)
|
||||
continue;
|
||||
toDo(event);
|
||||
}
|
||||
}
|
||||
|
||||
local_ads::Timestamp GetMinTimestamp(std::list<local_ads::Event> const & events,
|
||||
std::string const & countryId, int64_t mwmVersion)
|
||||
{
|
||||
local_ads::Timestamp minTimestamp = local_ads::Timestamp::max();
|
||||
FilterEvents(events, countryId, mwmVersion, [&minTimestamp](local_ads::Event const & event)
|
||||
{
|
||||
if (event.m_timestamp < minTimestamp)
|
||||
minTimestamp = event.m_timestamp;
|
||||
});
|
||||
return minTimestamp;
|
||||
}
|
||||
|
||||
local_ads::Timestamp GetMaxTimestamp(std::list<local_ads::Event> const & events,
|
||||
std::string const & countryId, int64_t mwmVersion)
|
||||
{
|
||||
local_ads::Timestamp maxTimestamp = local_ads::Timestamp::min();
|
||||
FilterEvents(events, countryId, mwmVersion, [&maxTimestamp](local_ads::Event const & event)
|
||||
{
|
||||
if (event.m_timestamp > maxTimestamp)
|
||||
maxTimestamp = event.m_timestamp;
|
||||
});
|
||||
return maxTimestamp;
|
||||
}
|
||||
|
||||
std::string GetPath(std::string const & fileName)
|
||||
{
|
||||
return base::JoinPath(GetPlatform().SettingsDir(), kStatisticsFolderName, fileName);
|
||||
}
|
||||
|
||||
std::string GetPath(local_ads::Event const & event)
|
||||
{
|
||||
return GetPath(event.m_countryId + "_" + strings::to_string(event.m_mwmVersion) + kStatisticsExt);
|
||||
}
|
||||
|
||||
std::string StatisticsFolder()
|
||||
{
|
||||
return GetPath("");
|
||||
}
|
||||
|
||||
void CreateDirIfNotExist()
|
||||
{
|
||||
std::string const statsFolder = StatisticsFolder();
|
||||
if (!GetPlatform().IsFileExistsByFullPath(statsFolder) && !Platform::MkDirChecked(statsFolder))
|
||||
MYTHROW(FileSystemException, ("Unable to find or create directory", statsFolder));
|
||||
}
|
||||
|
||||
std::list<local_ads::Event> ReadEvents(std::string const & fileName)
|
||||
{
|
||||
std::list<local_ads::Event> result;
|
||||
if (!GetPlatform().IsFileExistsByFullPath(fileName))
|
||||
return result;
|
||||
|
||||
try
|
||||
{
|
||||
FileReader reader(fileName);
|
||||
ReaderSource<FileReader> src(reader);
|
||||
ReadPackedData(src, [&result](local_ads::Statistics::PackedData && data,
|
||||
std::string const & countryId, int64_t mwmVersion,
|
||||
local_ads::Timestamp const & baseTimestamp) {
|
||||
auto const mercatorPt = Int64ToPointObsolete(data.m_mercator, kPointCoordBits);
|
||||
result.emplace_back(static_cast<local_ads::EventType>(data.m_eventType), mwmVersion, countryId,
|
||||
data.m_featureIndex, data.m_zoomLevel,
|
||||
baseTimestamp + std::chrono::seconds(data.m_seconds),
|
||||
mercator::YToLat(mercatorPt.y), mercator::XToLon(mercatorPt.x), data.m_accuracy);
|
||||
});
|
||||
}
|
||||
catch (Reader::Exception const & ex)
|
||||
{
|
||||
LOG(LWARNING, ("Error reading file:", fileName, ex.Msg()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string MakeRemoteURL(std::string const & userId, std::string const & name, int64_t version)
|
||||
{
|
||||
if (kStatisticsServer.empty())
|
||||
return {};
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << kStatisticsServer << "/";
|
||||
ss << url::UrlEncode(userId) << "/";
|
||||
ss << version << "/";
|
||||
ss << url::UrlEncode(name);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SerializeForServer(std::list<local_ads::Event> const & events,
|
||||
std::string const & userId)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
ASSERT(!events.empty(), ());
|
||||
auto root = base::NewJSONObject();
|
||||
ToJSONObject(*root, "userId", userId);
|
||||
ToJSONObject(*root, "countryId", events.front().m_countryId);
|
||||
ToJSONObject(*root, "mwmVersion", events.front().m_mwmVersion);
|
||||
auto eventsNode = base::NewJSONArray();
|
||||
for (auto const & event : events)
|
||||
{
|
||||
auto eventNode = base::NewJSONObject();
|
||||
auto s = duration_cast<seconds>(event.m_timestamp.time_since_epoch()).count();
|
||||
ToJSONObject(*eventNode, "type", static_cast<uint8_t>(event.m_type));
|
||||
ToJSONObject(*eventNode, "timestamp", static_cast<int64_t>(s));
|
||||
ToJSONObject(*eventNode, "featureId", static_cast<int32_t>(event.m_featureId));
|
||||
ToJSONObject(*eventNode, "zoomLevel", event.m_zoomLevel);
|
||||
ToJSONObject(*eventNode, "latitude", event.m_latitude);
|
||||
ToJSONObject(*eventNode, "longitude", event.m_longitude);
|
||||
ToJSONObject(*eventNode, "accuracyInMeters", event.m_accuracyInMeters);
|
||||
json_array_append_new(eventsNode.get(), eventNode.release());
|
||||
}
|
||||
json_object_set_new(root.get(), "events", eventsNode.release());
|
||||
std::unique_ptr<char, JSONFreeDeleter> buffer(
|
||||
json_dumps(root.get(), JSON_COMPACT | JSON_ENSURE_ASCII));
|
||||
std::vector<uint8_t> result;
|
||||
|
||||
using Deflate = coding::ZLib::Deflate;
|
||||
Deflate deflate(Deflate::Format::ZLib, Deflate::Level::BestCompression);
|
||||
deflate(buffer.get(), strlen(buffer.get()), std::back_inserter(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CanUpload()
|
||||
{
|
||||
auto const connectionStatus = GetPlatform().ConnectionStatus();
|
||||
if (connectionStatus == Platform::EConnectionType::CONNECTION_WIFI)
|
||||
return true;
|
||||
|
||||
return connectionStatus == Platform::EConnectionType::CONNECTION_WWAN &&
|
||||
platform::GetCurrentNetworkPolicy().CanUse();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace local_ads
|
||||
{
|
||||
Statistics::Statistics()
|
||||
: m_userId(GetPlatform().UniqueIdHash())
|
||||
{}
|
||||
|
||||
void Statistics::Startup()
|
||||
{
|
||||
m_isEnabled = true;
|
||||
GetPlatform().RunTask(Platform::Thread::File, [this]
|
||||
{
|
||||
IndexMetadata();
|
||||
SendToServer();
|
||||
});
|
||||
}
|
||||
|
||||
void Statistics::RegisterEvent(Event && event)
|
||||
{
|
||||
if (!m_isEnabled)
|
||||
return;
|
||||
|
||||
RegisterEvents({std::move(event)});
|
||||
}
|
||||
|
||||
void Statistics::RegisterEvents(std::list<Event> && events)
|
||||
{
|
||||
if (!m_isEnabled)
|
||||
return;
|
||||
GetPlatform().RunTask(Platform::Thread::File,
|
||||
std::bind(&Statistics::ProcessEvents, this, std::move(events)));
|
||||
}
|
||||
|
||||
void Statistics::RegisterEventSync(Event && event)
|
||||
{
|
||||
std::list<Event> events = {std::move(event)};
|
||||
ProcessEvents(events);
|
||||
}
|
||||
|
||||
void Statistics::SetEnabled(bool isEnabled)
|
||||
{
|
||||
m_isEnabled = isEnabled;
|
||||
}
|
||||
|
||||
std::list<Event> Statistics::WriteEvents(std::list<Event> & events, std::string & fileNameToRebuild)
|
||||
{
|
||||
try
|
||||
{
|
||||
CreateDirIfNotExist();
|
||||
if (m_metadataCache.empty())
|
||||
IndexMetadata();
|
||||
|
||||
std::unique_ptr<FileWriter> writer;
|
||||
|
||||
events.sort();
|
||||
|
||||
auto eventIt = events.begin();
|
||||
for (; eventIt != events.end(); ++eventIt)
|
||||
{
|
||||
Event const & event = *eventIt;
|
||||
MetadataKey const key = std::make_pair(event.m_countryId, event.m_mwmVersion);
|
||||
auto it = m_metadataCache.find(key);
|
||||
|
||||
// Get metadata.
|
||||
Metadata metadata;
|
||||
bool needWriteMetadata = false;
|
||||
if (it == m_metadataCache.end())
|
||||
{
|
||||
metadata.m_timestamp = GetMinTimestamp(events, event.m_countryId, event.m_mwmVersion);
|
||||
metadata.m_fileName = GetPath(event);
|
||||
m_metadataCache[key] = metadata;
|
||||
needWriteMetadata = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
metadata = it->second;
|
||||
}
|
||||
|
||||
if (writer == nullptr || writer->GetName() != metadata.m_fileName)
|
||||
{
|
||||
writer = std::make_unique<FileWriter>(
|
||||
metadata.m_fileName,
|
||||
needWriteMetadata ? FileWriter::OP_WRITE_TRUNCATE : FileWriter::OP_APPEND);
|
||||
}
|
||||
|
||||
if (needWriteMetadata)
|
||||
WriteMetadata(*writer, event.m_countryId, event.m_mwmVersion, metadata.m_timestamp);
|
||||
|
||||
// Check if timestamp is out of date. In this case we have to rebuild events package.
|
||||
using namespace std::chrono;
|
||||
int64_t const s = duration_cast<seconds>(event.m_timestamp - metadata.m_timestamp).count();
|
||||
if (s < 0 || s > kEventMaxLifetimeInSeconds)
|
||||
{
|
||||
fileNameToRebuild = writer->GetName();
|
||||
|
||||
// Return unprocessed events.
|
||||
std::list<Event> unprocessedEvents;
|
||||
unprocessedEvents.splice(unprocessedEvents.end(), events, eventIt, events.end());
|
||||
return unprocessedEvents;
|
||||
}
|
||||
|
||||
PackedData data;
|
||||
data.m_featureIndex = event.m_featureId;
|
||||
data.m_seconds = static_cast<uint32_t>(s);
|
||||
data.m_zoomLevel = event.m_zoomLevel;
|
||||
data.m_eventType = static_cast<uint8_t>(event.m_type);
|
||||
auto const mercatorPt = mercator::FromLatLon(event.m_latitude, event.m_longitude);
|
||||
data.m_mercator = PointToInt64Obsolete(mercatorPt, kPointCoordBits);
|
||||
data.m_accuracy = event.m_accuracyInMeters;
|
||||
WritePackedData(*writer, std::move(data));
|
||||
}
|
||||
}
|
||||
catch (RootException const & ex)
|
||||
{
|
||||
LOG(LWARNING, (ex.Msg()));
|
||||
}
|
||||
return std::list<Event>();
|
||||
}
|
||||
|
||||
void Statistics::ProcessEvents(std::list<Event> & events)
|
||||
{
|
||||
bool needRebuild;
|
||||
do
|
||||
{
|
||||
std::string fileNameToRebuild;
|
||||
auto unprocessedEvents = WriteEvents(events, fileNameToRebuild);
|
||||
needRebuild = !unprocessedEvents.empty();
|
||||
if (!needRebuild)
|
||||
break;
|
||||
|
||||
// The first event in the list is cause of writing interruption.
|
||||
Event event = unprocessedEvents.front();
|
||||
|
||||
// Read events and merge with unprocessed ones.
|
||||
std::list<Event> newEvents = ReadEvents(fileNameToRebuild);
|
||||
newEvents.splice(newEvents.end(), std::move(unprocessedEvents));
|
||||
newEvents.sort();
|
||||
|
||||
// Clip obsolete events.
|
||||
auto constexpr kLifetime = std::chrono::seconds(kEventMaxLifetimeInSeconds);
|
||||
auto const maxTimestamp = GetMaxTimestamp(newEvents, event.m_countryId, event.m_mwmVersion);
|
||||
auto newMinTimestamp = maxTimestamp - kLifetime + kDeletionPeriod;
|
||||
for (auto eventIt = newEvents.begin(); eventIt != newEvents.end();)
|
||||
{
|
||||
if (eventIt->m_countryId == event.m_countryId &&
|
||||
eventIt->m_mwmVersion == event.m_mwmVersion && eventIt->m_timestamp < newMinTimestamp)
|
||||
{
|
||||
eventIt = newEvents.erase(eventIt);
|
||||
}
|
||||
else
|
||||
{
|
||||
++eventIt;
|
||||
}
|
||||
}
|
||||
|
||||
// Update run-time cache and delete rebuilding file.
|
||||
m_metadataCache.erase(MetadataKey(event.m_countryId, event.m_mwmVersion));
|
||||
FileWriter::DeleteFileX(fileNameToRebuild);
|
||||
std::swap(events, newEvents);
|
||||
} while (needRebuild);
|
||||
}
|
||||
|
||||
void Statistics::SendToServer()
|
||||
{
|
||||
if (!m_isEnabled)
|
||||
return;
|
||||
|
||||
if (CanUpload())
|
||||
{
|
||||
for (auto it = m_metadataCache.begin(); it != m_metadataCache.end(); ++it)
|
||||
{
|
||||
auto metadataKey = it->first;
|
||||
auto metadata = it->second;
|
||||
GetPlatform().RunTask(Platform::Thread::Network, [this, metadataKey = std::move(metadataKey),
|
||||
metadata = std::move(metadata)]() mutable
|
||||
{
|
||||
SendFileWithMetadata(std::move(metadataKey), std::move(metadata));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Send every |kSendingTimeout|.
|
||||
GetPlatform().RunDelayedTask(Platform::Thread::File, kSendingTimeout, [this]
|
||||
{
|
||||
SendToServer();
|
||||
});
|
||||
}
|
||||
|
||||
void Statistics::SendFileWithMetadata(MetadataKey && metadataKey, Metadata && metadata)
|
||||
{
|
||||
std::string const url = MakeRemoteURL(m_userId, metadataKey.first, metadataKey.second);
|
||||
if (url.empty())
|
||||
return;
|
||||
|
||||
std::list<Event> events = ReadEvents(metadata.m_fileName);
|
||||
if (events.empty())
|
||||
return;
|
||||
|
||||
std::string contentType = "application/octet-stream";
|
||||
std::string contentEncoding = "";
|
||||
std::vector<uint8_t> bytes = SerializeForServer(events, m_userId);
|
||||
ASSERT(!bytes.empty(), ());
|
||||
|
||||
platform::HttpClient request(url);
|
||||
request.SetTimeout(5); // timeout in seconds
|
||||
#ifdef DEV_LOCAL_ADS_SERVER
|
||||
request.LoadHeaders(true);
|
||||
request.SetRawHeader("Host", "localads-statistics.maps.me");
|
||||
#endif
|
||||
request.SetBodyData(std::string(bytes.begin(), bytes.end()), contentType, "POST",
|
||||
contentEncoding);
|
||||
request.SetRawHeader("User-Agent", GetPlatform().GetAppUserAgent());
|
||||
if (request.RunHttpRequest() && request.ErrorCode() == 200)
|
||||
{
|
||||
GetPlatform().RunTask(Platform::Thread::File, [this, metadataKey = std::move(metadataKey),
|
||||
metadata = std::move(metadata)]
|
||||
{
|
||||
FileWriter::DeleteFileX(metadata.m_fileName);
|
||||
m_metadataCache.erase(metadataKey);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(LWARNING, ("Sending statistics failed:", "URL:", url, "Error code:", request.ErrorCode(),
|
||||
metadataKey.first, metadataKey.second));
|
||||
}
|
||||
}
|
||||
|
||||
std::list<Event> Statistics::WriteEventsForTesting(std::list<Event> const & events,
|
||||
std::string & fileNameToRebuild)
|
||||
{
|
||||
std::list<Event> mutableEvents = events;
|
||||
return WriteEvents(mutableEvents, fileNameToRebuild);
|
||||
}
|
||||
|
||||
void Statistics::IndexMetadata()
|
||||
{
|
||||
std::vector<std::string> files;
|
||||
GetPlatform().GetFilesByExt(StatisticsFolder(), kStatisticsExt, files);
|
||||
for (auto const & filename : files)
|
||||
ExtractMetadata(GetPath(filename));
|
||||
BalanceMemory();
|
||||
}
|
||||
|
||||
void Statistics::ExtractMetadata(std::string const & fileName)
|
||||
{
|
||||
ASSERT(GetPlatform().IsFileExistsByFullPath(fileName), ());
|
||||
try
|
||||
{
|
||||
std::string countryId;
|
||||
int64_t mwmVersion;
|
||||
Timestamp baseTimestamp;
|
||||
{
|
||||
FileReader reader(fileName);
|
||||
ReaderSource<FileReader> src(reader);
|
||||
ReadMetadata(src, countryId, mwmVersion, baseTimestamp);
|
||||
}
|
||||
|
||||
auto const expectedFileName =
|
||||
GetPath(countryId + "_" + strings::to_string(mwmVersion) + kStatisticsExt);
|
||||
|
||||
if (fileName != expectedFileName)
|
||||
{
|
||||
alohalytics::TStringMap const info = {
|
||||
{"expectedFilename", expectedFileName},
|
||||
{"actualFilename", fileName},
|
||||
};
|
||||
alohalytics::LogEvent("localAdsBadFile", info);
|
||||
}
|
||||
|
||||
MetadataKey const key = std::make_pair(countryId, mwmVersion);
|
||||
auto it = m_metadataCache.find(key);
|
||||
if (it != m_metadataCache.end())
|
||||
{
|
||||
// The only statistics file for countryId + mwmVersion must exist.
|
||||
if (it->second.m_timestamp < baseTimestamp)
|
||||
FileWriter::DeleteFileX(it->second.m_fileName);
|
||||
else
|
||||
FileWriter::DeleteFileX(fileName);
|
||||
}
|
||||
m_metadataCache[key] = Metadata(fileName, baseTimestamp);
|
||||
}
|
||||
catch (Reader::Exception const & ex)
|
||||
{
|
||||
LOG(LWARNING, ("Error reading file:", fileName, ex.Msg()));
|
||||
}
|
||||
}
|
||||
|
||||
void Statistics::BalanceMemory()
|
||||
{
|
||||
std::map<MetadataKey, uint64_t> sizeInBytes;
|
||||
uint64_t totalSize = 0;
|
||||
for (auto const & metadata : m_metadataCache)
|
||||
{
|
||||
FileReader reader(metadata.second.m_fileName);
|
||||
sizeInBytes[metadata.first] = reader.Size();
|
||||
totalSize += reader.Size();
|
||||
}
|
||||
|
||||
if (totalSize < kMaxFilesSizeInBytes)
|
||||
return;
|
||||
|
||||
auto constexpr kPackedDataSize =
|
||||
sizeof(PackedData::m_featureIndex) + sizeof(PackedData::m_seconds) +
|
||||
sizeof(PackedData::m_accuracy) + sizeof(PackedData::m_mercator) +
|
||||
sizeof(PackedData::m_zoomLevel) + sizeof(PackedData::m_eventType);
|
||||
for (auto const & metadata : sizeInBytes)
|
||||
{
|
||||
auto const disposingSize = static_cast<uint64_t>(metadata.second * kEventsDisposingRate);
|
||||
auto const disposingCount = disposingSize / kPackedDataSize;
|
||||
|
||||
std::string fileName = m_metadataCache[metadata.first].m_fileName;
|
||||
std::list<Event> events = ReadEvents(fileName);
|
||||
m_metadataCache.erase(metadata.first);
|
||||
FileWriter::DeleteFileX(fileName);
|
||||
if (events.size() <= disposingCount)
|
||||
continue;
|
||||
|
||||
events.sort();
|
||||
auto it = events.begin();
|
||||
std::advance(it, static_cast<size_t>(disposingCount));
|
||||
events.erase(events.begin(), it);
|
||||
|
||||
std::string fileNameToRebuild;
|
||||
WriteEvents(events, fileNameToRebuild);
|
||||
ASSERT(fileNameToRebuild.empty(), ());
|
||||
}
|
||||
}
|
||||
|
||||
std::list<Event> Statistics::ReadEventsForTesting(std::string const & fileName)
|
||||
{
|
||||
return ReadEvents(GetPath(fileName));
|
||||
}
|
||||
|
||||
void Statistics::ProcessEventsForTesting(std::list<Event> const & events)
|
||||
{
|
||||
std::list<Event> mutableEvents = events;
|
||||
ProcessEvents(mutableEvents);
|
||||
}
|
||||
|
||||
void Statistics::CleanupAfterTesting()
|
||||
{
|
||||
std::string const statsFolder = StatisticsFolder();
|
||||
if (GetPlatform().IsFileExistsByFullPath(statsFolder))
|
||||
GetPlatform().RmDirRecursively(statsFolder);
|
||||
}
|
||||
} // namespace local_ads
|
|
@ -1,77 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "local_ads/event.hpp"
|
||||
|
||||
#include "base/thread.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace local_ads
|
||||
{
|
||||
using ServerSerializer =
|
||||
std::function<std::vector<uint8_t>(std::list<Event> const & events, std::string const & userId,
|
||||
std::string & contentType, std::string & contentEncoding)>;
|
||||
|
||||
class Statistics final
|
||||
{
|
||||
public:
|
||||
struct PackedData
|
||||
{
|
||||
int64_t m_mercator = 0;
|
||||
uint32_t m_featureIndex = 0;
|
||||
uint32_t m_seconds = 0;
|
||||
uint16_t m_accuracy = 0;
|
||||
uint8_t m_eventType = 0;
|
||||
uint8_t m_zoomLevel = 0;
|
||||
};
|
||||
|
||||
Statistics();
|
||||
|
||||
void Startup();
|
||||
void RegisterEvent(Event && event);
|
||||
void RegisterEvents(std::list<Event> && events);
|
||||
// Save the event synchronously (for lightweight frawework only).
|
||||
void RegisterEventSync(Event && event);
|
||||
void SetEnabled(bool isEnabled);
|
||||
|
||||
std::list<Event> WriteEventsForTesting(std::list<Event> const & events,
|
||||
std::string & fileNameToRebuild);
|
||||
std::list<Event> ReadEventsForTesting(std::string const & fileName);
|
||||
void ProcessEventsForTesting(std::list<Event> const & events);
|
||||
void CleanupAfterTesting();
|
||||
|
||||
private:
|
||||
using MetadataKey = std::pair<std::string, int64_t>;
|
||||
struct Metadata
|
||||
{
|
||||
std::string m_fileName;
|
||||
Timestamp m_timestamp;
|
||||
|
||||
Metadata() = default;
|
||||
Metadata(std::string const & fileName, Timestamp const & timestamp)
|
||||
: m_fileName(fileName), m_timestamp(timestamp)
|
||||
{}
|
||||
};
|
||||
|
||||
void IndexMetadata();
|
||||
void ExtractMetadata(std::string const & fileName);
|
||||
void BalanceMemory();
|
||||
|
||||
std::list<Event> WriteEvents(std::list<Event> & events, std::string & fileNameToRebuild);
|
||||
void ProcessEvents(std::list<Event> & events);
|
||||
|
||||
void SendToServer();
|
||||
void SendFileWithMetadata(MetadataKey && metadataKey, Metadata && metadata);
|
||||
|
||||
std::string const m_userId;
|
||||
bool m_isEnabled = false;
|
||||
std::map<MetadataKey, Metadata> m_metadataCache;
|
||||
};
|
||||
} // namespace local_ads
|
|
@ -85,11 +85,6 @@ set(
|
|||
isolines_manager.hpp
|
||||
layers_statistics.cpp
|
||||
layers_statistics.hpp
|
||||
local_ads_manager.cpp
|
||||
local_ads_manager.hpp
|
||||
local_ads_mark.cpp
|
||||
local_ads_mark.hpp
|
||||
local_ads_supported_types.cpp
|
||||
mwm_url.cpp
|
||||
mwm_url.hpp
|
||||
notifications/notification_manager.cpp
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "map/bookmark_manager.hpp"
|
||||
#include "map/api_mark_point.hpp"
|
||||
#include "map/local_ads_mark.hpp"
|
||||
#include "map/routing_mark.hpp"
|
||||
#include "map/search_api.hpp"
|
||||
#include "map/search_mark.hpp"
|
||||
|
|
|
@ -269,7 +269,6 @@ void Framework::OnLocationUpdate(GpsInfo const & info)
|
|||
#endif
|
||||
|
||||
m_routingManager.OnLocationUpdate(rInfo);
|
||||
m_localAdsManager.OnLocationUpdate(rInfo, GetDrawScale());
|
||||
}
|
||||
|
||||
void Framework::OnCompassUpdate(CompassInfo const & info)
|
||||
|
@ -306,11 +305,6 @@ TrafficManager & Framework::GetTrafficManager()
|
|||
return m_trafficManager;
|
||||
}
|
||||
|
||||
LocalAdsManager & Framework::GetLocalAdsManager()
|
||||
{
|
||||
return m_localAdsManager;
|
||||
}
|
||||
|
||||
TransitReadManager & Framework::GetTransitManager()
|
||||
{
|
||||
return m_transitManager;
|
||||
|
@ -358,7 +352,6 @@ void Framework::OnViewportChanged(ScreenBase const & screen)
|
|||
|
||||
GetBookmarkManager().UpdateViewport(m_currentModelView);
|
||||
m_trafficManager.UpdateViewport(m_currentModelView);
|
||||
m_localAdsManager.UpdateViewport(m_currentModelView);
|
||||
m_transitManager.UpdateViewport(m_currentModelView);
|
||||
m_isolinesManager.UpdateViewport(m_currentModelView);
|
||||
m_guidesManager.UpdateViewport(m_currentModelView);
|
||||
|
@ -368,11 +361,7 @@ void Framework::OnViewportChanged(ScreenBase const & screen)
|
|||
}
|
||||
|
||||
Framework::Framework(FrameworkParams const & params)
|
||||
: m_localAdsManager(bind(&Framework::GetMwmsByRect, this, _1, true /* rough */),
|
||||
bind(&Framework::GetMwmIdByName, this, _1),
|
||||
bind(&Framework::ReadFeatures, this, _1, _2),
|
||||
bind(&Framework::GetMapObjectByID, this, _1))
|
||||
, m_enabledDiffs(params.m_enableDiffs)
|
||||
: m_enabledDiffs(params.m_enableDiffs)
|
||||
, m_isRenderingEnabled(true)
|
||||
, m_transitManager(m_featuresFetcher.GetDataSource(),
|
||||
[this](FeatureCallback const & fn, vector<FeatureID> const & features) {
|
||||
|
@ -502,14 +491,6 @@ Framework::Framework(FrameworkParams const & params)
|
|||
GetSearchAPI().LoadCitiesBoundaries();
|
||||
GetSearchAPI().CacheWorldLocalities();
|
||||
|
||||
// Local ads manager should be initialized after storage initialization.
|
||||
if (params.m_enableLocalAds)
|
||||
{
|
||||
auto const isActive = m_purchase->IsSubscriptionActive(SubscriptionType::RemoveAds);
|
||||
m_localAdsManager.Startup(m_bmManager.get(), !isActive);
|
||||
m_purchase->RegisterSubscription(&m_localAdsManager);
|
||||
}
|
||||
|
||||
m_routingManager.SetRouterImpl(RouterType::Vehicle);
|
||||
|
||||
UpdateMinBuildingsTapZoom();
|
||||
|
@ -665,7 +646,6 @@ void Framework::OnCountryFileDownloaded(storage::CountryId const & countryId,
|
|||
m_trafficManager.Invalidate();
|
||||
m_transitManager.Invalidate();
|
||||
m_isolinesManager.Invalidate();
|
||||
m_localAdsManager.OnDownloadCountry(countryId);
|
||||
InvalidateRect(rect);
|
||||
GetSearchAPI().ClearCaches();
|
||||
}
|
||||
|
@ -700,7 +680,6 @@ void Framework::OnMapDeregistered(platform::LocalCountryFile const & localFile)
|
|||
{
|
||||
auto action = [this, localFile]
|
||||
{
|
||||
m_localAdsManager.OnMwmDeregistered(localFile);
|
||||
m_transitManager.OnMwmDeregistered(localFile);
|
||||
m_isolinesManager.OnMwmDeregistered(localFile);
|
||||
m_trafficManager.OnMwmDeregistered(localFile);
|
||||
|
@ -1007,20 +986,6 @@ void Framework::FillInfoFromFeatureType(FeatureType & ft, place_page::Info & inf
|
|||
!info.IsNotEditableSponsored() && isMapVersionEditable;
|
||||
info.SetCanEditOrAdd(canEditOrAdd);
|
||||
|
||||
if (m_localAdsManager.IsSupportedType(info.GetTypes()))
|
||||
{
|
||||
info.SetLocalAdsUrl(m_localAdsManager.GetCompanyUrl(ft.GetID()));
|
||||
auto status = m_localAdsManager.HasAds(ft.GetID()) ? place_page::LocalAdsStatus::Customer
|
||||
: place_page::LocalAdsStatus::Candidate;
|
||||
if (status == place_page::LocalAdsStatus::Customer && !m_localAdsManager.HasVisualization(ft.GetID()))
|
||||
status = place_page::LocalAdsStatus::Hidden;
|
||||
info.SetLocalAdsStatus(status);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.SetLocalAdsStatus(place_page::LocalAdsStatus::NotAvailable);
|
||||
}
|
||||
|
||||
auto const latlon = mercator::ToLatLon(feature::GetCenter(ft));
|
||||
ASSERT(m_taxiEngine, ());
|
||||
info.SetReachableByTaxiProviders(m_taxiEngine->GetProvidersAtPos(latlon));
|
||||
|
@ -1837,17 +1802,16 @@ void Framework::FillSearchResultsMarks(search::Results::ConstIter begin,
|
|||
|
||||
if (r.m_details.m_isSponsoredHotel)
|
||||
{
|
||||
mark->SetBookingType(isFeature && m_localAdsManager.HasVisualization(r.GetFeatureID()) /* hasLocalAds */);
|
||||
mark->SetBookingType();
|
||||
mark->SetRating(r.m_details.m_hotelRating);
|
||||
mark->SetPricing(r.m_details.m_hotelPricing);
|
||||
}
|
||||
else if (isFeature)
|
||||
{
|
||||
bool const hasLocalAds = m_localAdsManager.HasVisualization(r.GetFeatureID());
|
||||
if (r.m_details.m_isHotel)
|
||||
mark->SetHotelType(hasLocalAds);
|
||||
mark->SetHotelType();
|
||||
else
|
||||
mark->SetFromType(r.GetFeatureType(), hasLocalAds);
|
||||
mark->SetFromType(r.GetFeatureType());
|
||||
auto product = GetProductInfo(r);
|
||||
if (product.m_ugcRating != search::ProductInfo::kInvalidRating)
|
||||
mark->SetRating(product.m_ugcRating);
|
||||
|
@ -1930,22 +1894,6 @@ void Framework::CreateDrapeEngine(ref_ptr<dp::GraphicsContextFactory> contextFac
|
|||
{
|
||||
if (events.empty())
|
||||
return;
|
||||
|
||||
list<local_ads::Event> statEvents;
|
||||
for (auto const & event : events)
|
||||
{
|
||||
auto const & mwmInfo = event.m_feature.m_mwmId.GetInfo();
|
||||
if (!mwmInfo || !m_localAdsManager.HasAds(event.m_feature))
|
||||
continue;
|
||||
|
||||
statEvents.emplace_back(local_ads::EventType::ShowPoint,
|
||||
mwmInfo->GetVersion(), mwmInfo->GetCountryName(),
|
||||
event.m_feature.m_index, event.m_zoomLevel, event.m_timestamp,
|
||||
mercator::YToLat(event.m_myPosition.y),
|
||||
mercator::XToLon(event.m_myPosition.x),
|
||||
static_cast<uint16_t>(event.m_gpsAccuracy));
|
||||
}
|
||||
m_localAdsManager.GetStatistics().RegisterEvents(std::move(statEvents));
|
||||
};
|
||||
|
||||
auto onGraphicsContextInitialized = [this]()
|
||||
|
@ -2035,7 +1983,6 @@ void Framework::CreateDrapeEngine(ref_ptr<dp::GraphicsContextFactory> contextFac
|
|||
m_transitManager.SetDrapeEngine(make_ref(m_drapeEngine));
|
||||
m_isolinesManager.SetDrapeEngine(make_ref(m_drapeEngine));
|
||||
m_guidesManager.SetDrapeEngine(make_ref(m_drapeEngine));
|
||||
m_localAdsManager.SetDrapeEngine(make_ref(m_drapeEngine));
|
||||
m_searchMarks.SetDrapeEngine(make_ref(m_drapeEngine));
|
||||
|
||||
InvalidateUserMarks();
|
||||
|
@ -2069,7 +2016,6 @@ void Framework::OnRecoverSurface(int width, int height, bool recreateContextDepe
|
|||
m_trafficManager.OnRecoverSurface();
|
||||
m_transitManager.Invalidate();
|
||||
m_isolinesManager.Invalidate();
|
||||
m_localAdsManager.Invalidate();
|
||||
}
|
||||
|
||||
void Framework::OnDestroySurface()
|
||||
|
@ -2104,7 +2050,6 @@ void Framework::DestroyDrapeEngine()
|
|||
m_transitManager.SetDrapeEngine(nullptr);
|
||||
m_isolinesManager.SetDrapeEngine(nullptr);
|
||||
m_guidesManager.SetDrapeEngine(nullptr);
|
||||
m_localAdsManager.SetDrapeEngine(nullptr);
|
||||
m_searchMarks.SetDrapeEngine(nullptr);
|
||||
GetBookmarkManager().SetDrapeEngine(nullptr);
|
||||
|
||||
|
@ -2211,7 +2156,6 @@ void Framework::SetMapStyle(MapStyle mapStyle)
|
|||
if (m_drapeEngine != nullptr)
|
||||
m_drapeEngine->UpdateMapStyle();
|
||||
InvalidateUserMarks();
|
||||
m_localAdsManager.Invalidate();
|
||||
UpdateMinBuildingsTapZoom();
|
||||
}
|
||||
|
||||
|
@ -2721,7 +2665,6 @@ std::optional<place_page::Info> Framework::BuildPlacePageInfo(
|
|||
return {};
|
||||
|
||||
outInfo.SetBuildInfo(buildInfo);
|
||||
outInfo.SetAdsEngine(m_adsEngine.get());
|
||||
|
||||
if (buildInfo.IsUserMarkMatchingEnabled())
|
||||
{
|
||||
|
@ -4063,8 +4006,6 @@ search::ProductInfo Framework::GetProductInfo(search::Result const & result) con
|
|||
|
||||
search::ProductInfo productInfo;
|
||||
|
||||
productInfo.m_isLocalAdsCustomer = m_localAdsManager.HasVisualization(result.GetFeatureID());
|
||||
|
||||
auto const ugc = m_ugcApi->GetLoader().GetUGC(result.GetFeatureID());
|
||||
productInfo.m_ugcRating = ugc.m_totalRating;
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "map/features_fetcher.hpp"
|
||||
#include "map/guides_manager.hpp"
|
||||
#include "map/isolines_manager.hpp"
|
||||
#include "map/local_ads_manager.hpp"
|
||||
#include "map/mwm_url.hpp"
|
||||
#include "map/notifications/notification_manager.hpp"
|
||||
#include "map/place_page_info.hpp"
|
||||
|
@ -142,14 +141,12 @@ class NotificationCandidate;
|
|||
|
||||
struct FrameworkParams
|
||||
{
|
||||
bool m_enableLocalAds = true;
|
||||
bool m_enableDiffs = true;
|
||||
size_t m_numSearchAPIThreads = 1;
|
||||
|
||||
FrameworkParams() = default;
|
||||
FrameworkParams(bool enableLocalAds, bool enableDiffs)
|
||||
: m_enableLocalAds(enableLocalAds)
|
||||
, m_enableDiffs(enableDiffs)
|
||||
FrameworkParams(bool enableDiffs)
|
||||
: m_enableDiffs(enableDiffs)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -199,8 +196,6 @@ protected:
|
|||
// search::Engine and, therefore, destroyed after search::Engine.
|
||||
std::unique_ptr<storage::CountryInfoGetter> m_infoGetter;
|
||||
|
||||
LocalAdsManager m_localAdsManager;
|
||||
|
||||
// The order matters here: ugc::Api should be destroyed after
|
||||
// SearchAPI and notifications::NotificationManager.
|
||||
std::unique_ptr<ugc::Api> m_ugcApi;
|
||||
|
@ -772,8 +767,6 @@ public:
|
|||
|
||||
TrafficManager & GetTrafficManager();
|
||||
|
||||
LocalAdsManager & GetLocalAdsManager();
|
||||
|
||||
TransitReadManager & GetTransitManager();
|
||||
|
||||
IsolinesManager & GetIsolinesManager();
|
||||
|
|
|
@ -47,18 +47,6 @@ Framework::Framework(RequestTypeMask request) : m_request(request)
|
|||
request ^= REQUEST_TYPE_LOCATION;
|
||||
}
|
||||
|
||||
if (request & REQUEST_TYPE_LOCAL_ADS_FEATURES)
|
||||
{
|
||||
m_localAdsFeaturesReader = std::make_unique<LocalAdsFeaturesReader>();
|
||||
request ^= REQUEST_TYPE_LOCAL_ADS_FEATURES;
|
||||
}
|
||||
|
||||
if (request & REQUEST_TYPE_LOCAL_ADS_STATISTICS)
|
||||
{
|
||||
m_localAdsStatistics = std::make_unique<Statistics>();
|
||||
request ^= REQUEST_TYPE_LOCAL_ADS_STATISTICS;
|
||||
}
|
||||
|
||||
if (request & REQUEST_TYPE_NOTIFICATION)
|
||||
{
|
||||
request ^= REQUEST_TYPE_NOTIFICATION;
|
||||
|
@ -105,21 +93,6 @@ CountryInfoReader::Info Framework::GetLocation(m2::PointD const & pt) const
|
|||
return m_countryInfoReader->GetMwmInfo(pt);
|
||||
}
|
||||
|
||||
std::vector<CampaignFeature> Framework::GetLocalAdsFeatures(double lat, double lon,
|
||||
double radiusInMeters, uint32_t maxCount)
|
||||
{
|
||||
ASSERT(m_request & REQUEST_TYPE_LOCAL_ADS_FEATURES, (m_request));
|
||||
CHECK(m_localAdsFeaturesReader, ());
|
||||
return m_localAdsFeaturesReader->GetCampaignFeatures(lat, lon, radiusInMeters, maxCount);
|
||||
}
|
||||
|
||||
Statistics * Framework::GetLocalAdsStatistics()
|
||||
{
|
||||
ASSERT(m_request & REQUEST_TYPE_LOCAL_ADS_STATISTICS, (m_request));
|
||||
CHECK(m_localAdsStatistics, ());
|
||||
return m_localAdsStatistics.get();
|
||||
}
|
||||
|
||||
notifications::Notification Framework::GetNotification() const
|
||||
{
|
||||
// Do not disturb from 9p.m. to 10 a.m.
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "map/bookmark_manager.hpp"
|
||||
#include "map/local_ads_manager.hpp"
|
||||
#include "map/notifications/notification_manager.hpp"
|
||||
#include "map/user.hpp"
|
||||
|
||||
|
@ -33,8 +32,8 @@ enum RequestType
|
|||
// and takes much time. For example it takes ~50ms on LG Nexus 5, ~100ms on Samsung A5, ~200ms on
|
||||
// Fly IQ4403.
|
||||
REQUEST_TYPE_LOCATION = 1u << 4,
|
||||
REQUEST_TYPE_LOCAL_ADS_FEATURES = 1u << 5,
|
||||
REQUEST_TYPE_LOCAL_ADS_STATISTICS = 1u << 6,
|
||||
// REQUEST_TYPE_LOCAL_ADS_FEATURES = 1u << 5,
|
||||
// REQUEST_TYPE_LOCAL_ADS_STATISTICS = 1u << 6,
|
||||
REQUEST_TYPE_NOTIFICATION = 1u << 7,
|
||||
};
|
||||
|
||||
|
@ -64,9 +63,6 @@ public:
|
|||
size_t GetNumberOfUnsentEdits() const;
|
||||
bool IsBookmarksCloudEnabled() const;
|
||||
CountryInfoReader::Info GetLocation(m2::PointD const & pt) const;
|
||||
std::vector<CampaignFeature> GetLocalAdsFeatures(double lat, double lon, double radiusInMeters,
|
||||
uint32_t maxCount);
|
||||
Statistics * GetLocalAdsStatistics();
|
||||
notifications::Notification GetNotification() const;
|
||||
|
||||
private:
|
||||
|
@ -77,8 +73,6 @@ private:
|
|||
size_t m_numberOfUnsentEdits = 0;
|
||||
bool m_bookmarksCloudEnabled = false;
|
||||
std::unique_ptr<CountryInfoReader> m_countryInfoReader;
|
||||
std::unique_ptr<LocalAdsFeaturesReader> m_localAdsFeaturesReader;
|
||||
std::unique_ptr<Statistics> m_localAdsStatistics;
|
||||
};
|
||||
|
||||
std::string FeatureParamsToString(int64_t mwmVersion, std::string const & countryId, uint32_t featureIndex);
|
||||
|
|
|
@ -1,965 +0,0 @@
|
|||
#include "map/local_ads_manager.hpp"
|
||||
#include "map/bookmark_manager.hpp"
|
||||
#include "map/local_ads_mark.hpp"
|
||||
|
||||
#include "local_ads/campaign_serialization.hpp"
|
||||
#include "local_ads/config.hpp"
|
||||
#include "local_ads/file_helpers.hpp"
|
||||
#include "local_ads/icons_info.hpp"
|
||||
|
||||
#include "drape_frontend/drape_engine.hpp"
|
||||
#include "drape_frontend/visual_params.hpp"
|
||||
|
||||
#include "indexer/feature_algo.hpp"
|
||||
#include "indexer/feature_data.hpp"
|
||||
#include "indexer/scales.hpp"
|
||||
|
||||
#include "platform/http_client.hpp"
|
||||
#include "platform/marketing_service.hpp"
|
||||
#include "platform/platform.hpp"
|
||||
#include "platform/preferred_languages.hpp"
|
||||
#include "platform/settings.hpp"
|
||||
|
||||
#include "coding/point_coding.hpp"
|
||||
#include "coding/string_utf8_multilang.hpp"
|
||||
#include "coding/url.hpp"
|
||||
|
||||
#include "base/file_name_utils.hpp"
|
||||
|
||||
#include "private.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
|
||||
using namespace base;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::array<char const * const, 5> const kMarketingParameters = {{marketing::kFrom, marketing::kType, marketing::kName,
|
||||
marketing::kContent, marketing::kKeyword}};
|
||||
std::string const kServerUrl = LOCAL_ADS_SERVER_URL;
|
||||
std::string const kCampaignPageUrl = LOCAL_ADS_COMPANY_PAGE_URL;
|
||||
|
||||
std::string const kCampaignFile = "local_ads_campaigns.dat";
|
||||
std::string const kCampaignFeaturesFile = "local_ads_features.dat";
|
||||
std::string const kLocalAdsSymbolsFile = "local_ads_symbols.txt";
|
||||
auto constexpr kWWanUpdateTimeout = std::chrono::hours(12);
|
||||
uint8_t constexpr kRequestMinZoomLevel = 12;
|
||||
auto constexpr kFailedDownloadingTimeout = std::chrono::seconds(2);
|
||||
auto constexpr kMaxDownloadingAttempts = 5;
|
||||
|
||||
auto constexpr kHiddenFeaturePriority = 1;
|
||||
|
||||
auto constexpr kMinCheckInterval = std::chrono::minutes(1);
|
||||
double constexpr kMinCheckDistanceInMeters = 5.0;
|
||||
double constexpr kMinSearchRadiusInMeters = 20.0;
|
||||
double constexpr kMaxAllowableAccuracyInMeters = 20.0;
|
||||
uint32_t constexpr kMaxHitCount = 3;
|
||||
|
||||
void SerializeCampaign(FileWriter & writer, std::string const & countryName,
|
||||
LocalAdsManager::Timestamp const & ts,
|
||||
std::vector<uint8_t> const & rawData)
|
||||
{
|
||||
local_ads::WriteCountryName(writer, countryName);
|
||||
local_ads::WriteTimestamp<std::chrono::hours>(writer, ts);
|
||||
local_ads::WriteRawData(writer, rawData);
|
||||
}
|
||||
|
||||
void DeserializeCampaign(ReaderSource<FileReader> & src, std::string & countryName,
|
||||
LocalAdsManager::Timestamp & ts, std::vector<uint8_t> & rawData)
|
||||
{
|
||||
countryName = local_ads::ReadCountryName(src);
|
||||
ts = local_ads::ReadTimestamp<std::chrono::hours>(src);
|
||||
rawData = local_ads::ReadRawData(src);
|
||||
}
|
||||
|
||||
std::string GetPath(std::string const & fileName)
|
||||
{
|
||||
return base::JoinPath(GetPlatform().SettingsDir(), fileName);
|
||||
}
|
||||
|
||||
std::string MakeCampaignDownloadingURL(MwmSet::MwmId const & mwmId)
|
||||
{
|
||||
if (kServerUrl.empty() || !mwmId.IsAlive())
|
||||
return {};
|
||||
|
||||
std::ostringstream ss;
|
||||
auto const campaignDataVersion = static_cast<uint32_t>(local_ads::Version::Latest);
|
||||
ss << kServerUrl << "/"
|
||||
<< campaignDataVersion << "/"
|
||||
<< mwmId.GetInfo()->GetVersion() << "/"
|
||||
<< url::UrlEncode(mwmId.GetInfo()->GetCountryName()) << ".ads";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GetCustomIcon(osm::MapObject const & mapObject)
|
||||
{
|
||||
auto const & metadata = mapObject.GetMetadata();
|
||||
auto const websiteStr = metadata.Get(feature::Metadata::FMD_WEBSITE);
|
||||
if (websiteStr.find("burgerking") != std::string::npos)
|
||||
return "0_burger-king";
|
||||
|
||||
auto const bannerUrl = metadata.Get(feature::Metadata::FMD_BANNER_URL);
|
||||
if (bannerUrl.find("mcarthurglen") != std::string::npos)
|
||||
return "partner1-l";
|
||||
|
||||
if (bannerUrl.find("sixt") != std::string::npos)
|
||||
return "partner2-l";
|
||||
|
||||
if (bannerUrl.find("azbuka_vkusa") != std::string::npos)
|
||||
return "partner12-l";
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string MakeCampaignPageURL(FeatureID const & featureId)
|
||||
{
|
||||
if (kCampaignPageUrl.empty() || !featureId.m_mwmId.IsAlive())
|
||||
return {};
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << kCampaignPageUrl << "/" << featureId.m_mwmId.GetInfo()->GetVersion() << "/"
|
||||
<< url::UrlEncode(featureId.m_mwmId.GetInfo()->GetCountryName()) << "/" << featureId.m_index;
|
||||
|
||||
url::Params params;
|
||||
params.reserve(kMarketingParameters.size());
|
||||
for (auto const & key : kMarketingParameters)
|
||||
{
|
||||
std::string value;
|
||||
if (!marketing::Settings::Get(key, value))
|
||||
continue;
|
||||
|
||||
params.push_back({key, value});
|
||||
}
|
||||
|
||||
return url::Make(ss.str(), params);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace features_cache
|
||||
{
|
||||
enum class Version : uint8_t
|
||||
{
|
||||
V0 = 0,
|
||||
Latest = V0
|
||||
};
|
||||
|
||||
struct PackedCampaignFeature
|
||||
{
|
||||
PackedCampaignFeature() = default;
|
||||
PackedCampaignFeature(uint32_t featureIndex, int64_t mercator)
|
||||
: m_featureIndex(featureIndex)
|
||||
, m_mercator(mercator)
|
||||
{}
|
||||
|
||||
uint32_t m_featureIndex = 0;
|
||||
int64_t m_mercator = 0;
|
||||
};
|
||||
|
||||
void SerializeVersion(FileWriter & writer, Version version)
|
||||
{
|
||||
WriteToSink(writer, static_cast<uint8_t>(version));
|
||||
}
|
||||
|
||||
void SerializeMwmData(FileWriter & writer, std::string const & countryId, int64_t mwmVersion)
|
||||
{
|
||||
local_ads::WriteCountryName(writer, countryId);
|
||||
local_ads::WriteZigZag(writer, mwmVersion);
|
||||
}
|
||||
|
||||
void SerializePackedFeatures(FileWriter & writer, std::vector<PackedCampaignFeature> const & packedFeatures)
|
||||
{
|
||||
auto const featuresCount = static_cast<uint32_t>(packedFeatures.size());
|
||||
WriteToSink(writer, featuresCount);
|
||||
for (auto const & data : packedFeatures)
|
||||
{
|
||||
WriteToSink(writer, data.m_featureIndex);
|
||||
local_ads::WriteZigZag(writer, data.m_mercator);
|
||||
}
|
||||
}
|
||||
|
||||
Version DeserializeVersion(ReaderSource<FileReader> & src)
|
||||
{
|
||||
return static_cast<Version>(ReadPrimitiveFromSource<uint8_t>(src));
|
||||
}
|
||||
|
||||
void DeserializeMwmData(ReaderSource<FileReader> & src, std::string & countryId, int64_t & mwmVersion)
|
||||
{
|
||||
countryId = local_ads::ReadCountryName(src);
|
||||
mwmVersion = local_ads::ReadZigZag(src);
|
||||
}
|
||||
|
||||
void DeserializePackedFeatures(ReaderSource<FileReader> & src, std::vector<PackedCampaignFeature> & packedFeatures)
|
||||
{
|
||||
auto const featuresCount = ReadPrimitiveFromSource<uint32_t>(src);
|
||||
packedFeatures.clear();
|
||||
packedFeatures.reserve(static_cast<size_t>(featuresCount));
|
||||
for (uint32_t i = 0; i < featuresCount; ++i)
|
||||
{
|
||||
PackedCampaignFeature data;
|
||||
data.m_featureIndex = ReadPrimitiveFromSource<uint32_t>(src);
|
||||
data.m_mercator = local_ads::ReadZigZag(src);
|
||||
packedFeatures.push_back(data);
|
||||
}
|
||||
}
|
||||
} // namespace features_cache
|
||||
|
||||
LocalAdsManager::LocalAdsManager(GetMwmsByRectFn && getMwmsByRectFn,
|
||||
GetMwmIdByNameFn && getMwmIdByName,
|
||||
ReadFeaturesFn && readFeaturesFn,
|
||||
GetMapObjectByIdFn && getMapObjectByIdFn)
|
||||
: m_getMwmsByRectFn(std::move(getMwmsByRectFn))
|
||||
, m_getMwmIdByNameFn(std::move(getMwmIdByName))
|
||||
, m_readFeaturesFn(std::move(readFeaturesFn))
|
||||
, m_getMapObjectByIdFn(std::move(getMapObjectByIdFn))
|
||||
, m_isStarted(false)
|
||||
, m_bmManager(nullptr)
|
||||
{
|
||||
CHECK(m_getMwmsByRectFn != nullptr, ());
|
||||
CHECK(m_getMwmIdByNameFn != nullptr, ());
|
||||
CHECK(m_readFeaturesFn != nullptr, ());
|
||||
CHECK(m_getMapObjectByIdFn != nullptr, ());
|
||||
}
|
||||
|
||||
void LocalAdsManager::Startup(BookmarkManager * bmManager, bool isEnabled)
|
||||
{
|
||||
m_isEnabled = isEnabled;
|
||||
FillSupportedTypes();
|
||||
m_bmManager = bmManager;
|
||||
|
||||
if (isEnabled)
|
||||
Start();
|
||||
}
|
||||
|
||||
void LocalAdsManager::Start()
|
||||
{
|
||||
m_isStarted = true;
|
||||
GetPlatform().RunTask(Platform::Thread::File, [this]
|
||||
{
|
||||
local_ads::IconsInfo::Instance().SetSourceFile(kLocalAdsSymbolsFile);
|
||||
|
||||
std::string const campaignFile = GetPath(kCampaignFile);
|
||||
|
||||
// Read persistence data.
|
||||
ReadCampaignFile(campaignFile);
|
||||
InvalidateImpl();
|
||||
});
|
||||
|
||||
m_statistics.Startup();
|
||||
}
|
||||
|
||||
void LocalAdsManager::SetDrapeEngine(ref_ptr<df::DrapeEngine> engine)
|
||||
{
|
||||
m_drapeEngine.Set(engine);
|
||||
UpdateFeaturesCache({});
|
||||
}
|
||||
|
||||
void LocalAdsManager::UpdateViewport(ScreenBase const & screen)
|
||||
{
|
||||
auto const connectionStatus = GetPlatform().ConnectionStatus();
|
||||
if (!m_isStarted || !m_isEnabled || kServerUrl.empty() ||
|
||||
connectionStatus == Platform::EConnectionType::CONNECTION_NONE ||
|
||||
df::GetZoomLevel(screen.GetScale()) < kRequestMinZoomLevel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// This function must be called on UI-thread.
|
||||
auto mwms = m_getMwmsByRectFn(screen.ClipRect());
|
||||
if (mwms.empty())
|
||||
return;
|
||||
|
||||
if (m_lastMwms == mwms)
|
||||
return;
|
||||
|
||||
m_lastMwms = mwms;
|
||||
|
||||
// Request local ads campaigns.
|
||||
GetPlatform().RunTask(Platform::Thread::File, [this, mwms = std::move(mwms)]()
|
||||
{
|
||||
RequestCampaigns(mwms);
|
||||
});
|
||||
}
|
||||
|
||||
void LocalAdsManager::RequestCampaigns(std::vector<MwmSet::MwmId> const & mwmIds)
|
||||
{
|
||||
auto const connectionStatus = GetPlatform().ConnectionStatus();
|
||||
|
||||
std::vector<MwmSet::MwmId> requestedCampaigns;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_campaignsMutex);
|
||||
for (auto const & mwm : mwmIds)
|
||||
{
|
||||
auto info = mwm.GetInfo();
|
||||
if (!info)
|
||||
continue;
|
||||
|
||||
// Skip currently downloading mwms.
|
||||
if (m_downloadingMwms.find(mwm) != m_downloadingMwms.cend())
|
||||
continue;
|
||||
|
||||
// Skip downloading request if maximum attempts count has been reached or
|
||||
// we are waiting for new attempt.
|
||||
auto const failedDownloadsIt = m_failedDownloads.find(mwm);
|
||||
if (failedDownloadsIt != m_failedDownloads.cend() && !failedDownloadsIt->second.CanRetry())
|
||||
continue;
|
||||
|
||||
std::string const & mwmName = info->GetCountryName();
|
||||
auto campaignIt = m_campaigns.find(mwmName);
|
||||
if (campaignIt == m_campaigns.end())
|
||||
{
|
||||
requestedCampaigns.push_back(mwm);
|
||||
m_downloadingMwms.insert(mwm);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If a campaign has not been requested from server this session.
|
||||
if (!campaignIt->second)
|
||||
{
|
||||
auto const it = m_info.find(mwmName);
|
||||
bool needUpdateByTimeout = (connectionStatus == Platform::EConnectionType::CONNECTION_WIFI);
|
||||
if (!needUpdateByTimeout && it != m_info.end())
|
||||
needUpdateByTimeout = local_ads::Clock::now() > (it->second.m_created + kWWanUpdateTimeout);
|
||||
|
||||
if (needUpdateByTimeout || it == m_info.end())
|
||||
{
|
||||
requestedCampaigns.push_back(mwm);
|
||||
m_downloadingMwms.insert(mwm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (requestedCampaigns.empty())
|
||||
return;
|
||||
|
||||
std::set<Request> requests;
|
||||
for (auto const & campaign : requestedCampaigns)
|
||||
requests.insert(std::make_pair(campaign, RequestType::Download));
|
||||
ProcessRequests(std::move(requests));
|
||||
}
|
||||
|
||||
bool LocalAdsManager::DownloadCampaign(MwmSet::MwmId const & mwmId, std::vector<uint8_t> & bytes)
|
||||
{
|
||||
bytes.clear();
|
||||
|
||||
std::string const url = MakeCampaignDownloadingURL(mwmId);
|
||||
if (url.empty())
|
||||
return true; // In this case empty result is valid.
|
||||
|
||||
// Skip already downloaded campaigns.
|
||||
auto const & countryName = mwmId.GetInfo()->GetCountryName();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_campaignsMutex);
|
||||
auto const it = m_campaigns.find(countryName);
|
||||
if (it != m_campaigns.cend() && it->second)
|
||||
return false;
|
||||
}
|
||||
|
||||
platform::HttpClient request(url);
|
||||
request.SetTimeout(5); // timeout in seconds
|
||||
request.SetRawHeader("User-Agent", GetPlatform().GetAppUserAgent());
|
||||
bool const success = request.RunHttpRequest() && request.ErrorCode() == 200;
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_campaignsMutex);
|
||||
m_downloadingMwms.erase(mwmId);
|
||||
if (!success)
|
||||
{
|
||||
bool const isAbsent = request.ErrorCode() == 404;
|
||||
auto const it = m_failedDownloads.find(mwmId);
|
||||
if (it == m_failedDownloads.cend())
|
||||
{
|
||||
m_failedDownloads.insert(std::make_pair(mwmId, BackoffStats(std::chrono::steady_clock::now(),
|
||||
kFailedDownloadingTimeout,
|
||||
1 /* m_attemptsCount */, isAbsent)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here we increase timeout multiplying by 2.
|
||||
it->second.m_currentTimeout = std::chrono::seconds(it->second.m_currentTimeout.count() * 2);
|
||||
it->second.m_attemptsCount++;
|
||||
it->second.m_fileIsAbsent = isAbsent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
m_failedDownloads.erase(mwmId);
|
||||
auto const & response = request.ServerResponse();
|
||||
bytes.assign(response.cbegin(), response.cend());
|
||||
return true;
|
||||
}
|
||||
|
||||
void LocalAdsManager::ProcessRequests(std::set<Request> && requests)
|
||||
{
|
||||
GetPlatform().RunTask(Platform::Thread::Network, [this, requests = std::move(requests)]
|
||||
{
|
||||
for (auto const & request : requests)
|
||||
{
|
||||
auto const & mwm = request.first;
|
||||
auto const & type = request.second;
|
||||
|
||||
if (!mwm.GetInfo())
|
||||
continue;
|
||||
|
||||
std::string const & countryName = mwm.GetInfo()->GetCountryName();
|
||||
if (type == RequestType::Download)
|
||||
{
|
||||
// Download campaign data from server.
|
||||
CampaignInfo info;
|
||||
info.m_created = local_ads::Clock::now();
|
||||
if (!DownloadCampaign(mwm, info.m_data))
|
||||
continue;
|
||||
|
||||
// Parse data and recreate marks.
|
||||
ClearLocalAdsForMwm(mwm);
|
||||
if (!info.m_data.empty())
|
||||
{
|
||||
auto campaignData = ParseCampaign(info.m_data, mwm, info.m_created, m_getMapObjectByIdFn);
|
||||
if (!campaignData.empty())
|
||||
{
|
||||
UpdateFeaturesCache(ReadCampaignFeatures(campaignData));
|
||||
CreateLocalAdsMarks(std::move(campaignData));
|
||||
}
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_campaignsMutex);
|
||||
m_campaigns[countryName] = true;
|
||||
m_info[countryName] = info;
|
||||
}
|
||||
else if (type == RequestType::Delete)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_campaignsMutex);
|
||||
m_campaigns.erase(countryName);
|
||||
m_info.erase(countryName);
|
||||
ClearLocalAdsForMwm(mwm);
|
||||
}
|
||||
}
|
||||
|
||||
auto const campaignFile = GetPath(kCampaignFile);
|
||||
auto const featureFile = GetPath(kCampaignFeaturesFile);
|
||||
|
||||
// Save data persistently.
|
||||
GetPlatform().RunTask(Platform::Thread::File, [this, campaignFile, featureFile]
|
||||
{
|
||||
WriteCampaignFile(campaignFile);
|
||||
WriteFeaturesFile(featureFile);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void LocalAdsManager::OnDownloadCountry(std::string const & countryName)
|
||||
{
|
||||
m_lastMwms.clear();
|
||||
GetPlatform().RunTask(Platform::Thread::File, [this, countryName]
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_campaignsMutex);
|
||||
m_campaigns.erase(countryName);
|
||||
m_info.erase(countryName);
|
||||
});
|
||||
}
|
||||
|
||||
void LocalAdsManager::OnMwmDeregistered(platform::LocalCountryFile const & countryFile)
|
||||
{
|
||||
m_lastMwms.clear();
|
||||
MwmSet::MwmId mwmId;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_featuresCacheMutex);
|
||||
for (auto const & cachedMwm : m_featuresCache)
|
||||
{
|
||||
if (cachedMwm.first.m_mwmId.IsDeregistered(countryFile))
|
||||
{
|
||||
mwmId = cachedMwm.first.m_mwmId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!mwmId.GetInfo())
|
||||
return;
|
||||
GetPlatform().RunTask(Platform::Thread::File, [this, mwmId]
|
||||
{
|
||||
ProcessRequests({std::make_pair(mwmId, RequestType::Delete)});
|
||||
});
|
||||
}
|
||||
|
||||
void LocalAdsManager::ReadCampaignFile(std::string const & campaignFile)
|
||||
{
|
||||
if (!GetPlatform().IsFileExistsByFullPath(campaignFile))
|
||||
return;
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_campaignsMutex);
|
||||
try
|
||||
{
|
||||
FileReader reader(campaignFile);
|
||||
ReaderSource<FileReader> src(reader);
|
||||
while (src.Size() > 0)
|
||||
{
|
||||
std::string countryName;
|
||||
CampaignInfo info;
|
||||
DeserializeCampaign(src, countryName, info.m_created, info.m_data);
|
||||
m_info[countryName] = info;
|
||||
m_campaigns[countryName] = false;
|
||||
}
|
||||
}
|
||||
catch (Reader::Exception const & ex)
|
||||
{
|
||||
LOG(LWARNING, ("Error reading file:", campaignFile, ex.Msg()));
|
||||
FileWriter::DeleteFileX(campaignFile);
|
||||
m_info.clear();
|
||||
m_campaigns.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void LocalAdsManager::WriteCampaignFile(std::string const & campaignFile)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_campaignsMutex);
|
||||
try
|
||||
{
|
||||
FileWriter writer(campaignFile);
|
||||
for (auto const & info : m_info)
|
||||
SerializeCampaign(writer, info.first, info.second.m_created, info.second.m_data);
|
||||
}
|
||||
catch (RootException const & ex)
|
||||
{
|
||||
LOG(LWARNING, ("Error writing file:", campaignFile, ex.Msg()));
|
||||
}
|
||||
}
|
||||
|
||||
void LocalAdsManager::WriteFeaturesFile(std::string const & featuresFile)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_featuresCacheMutex);
|
||||
try
|
||||
{
|
||||
FileWriter writer(featuresFile);
|
||||
features_cache::SerializeVersion(writer, features_cache::Version::V0);
|
||||
|
||||
MwmSet::MwmId lastMwm;
|
||||
std::vector<features_cache::PackedCampaignFeature> packedData;
|
||||
for (auto const & entry : m_featuresCache)
|
||||
{
|
||||
FeatureID const & fid = entry.first;
|
||||
if (lastMwm != fid.m_mwmId && !packedData.empty())
|
||||
{
|
||||
features_cache::SerializeMwmData(writer, lastMwm.GetInfo()->GetCountryName(), lastMwm.GetInfo()->GetVersion());
|
||||
features_cache::SerializePackedFeatures(writer, packedData);
|
||||
packedData.clear();
|
||||
}
|
||||
lastMwm = fid.m_mwmId;
|
||||
packedData.emplace_back(fid.m_index,
|
||||
PointToInt64Obsolete(entry.second.m_position, kPointCoordBits));
|
||||
}
|
||||
if (!packedData.empty())
|
||||
{
|
||||
features_cache::SerializeMwmData(writer, lastMwm.GetInfo()->GetCountryName(), lastMwm.GetInfo()->GetVersion());
|
||||
features_cache::SerializePackedFeatures(writer, packedData);
|
||||
}
|
||||
}
|
||||
catch (RootException const & ex)
|
||||
{
|
||||
LOG(LWARNING, ("Error writing file:", featuresFile, ex.Msg()));
|
||||
}
|
||||
}
|
||||
|
||||
void LocalAdsManager::Invalidate()
|
||||
{
|
||||
m_lastMwms.clear();
|
||||
GetPlatform().RunTask(Platform::Thread::File, [this]
|
||||
{
|
||||
InvalidateImpl();
|
||||
});
|
||||
}
|
||||
|
||||
void LocalAdsManager::InvalidateImpl()
|
||||
{
|
||||
DeleteAllLocalAdsMarks();
|
||||
m_drapeEngine.SafeCall(&df::DrapeEngine::RemoveAllCustomFeatures);
|
||||
|
||||
CampaignData campaignData;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_campaignsMutex);
|
||||
for (auto const & info : m_info)
|
||||
{
|
||||
auto data = ParseCampaign(info.second.m_data, m_getMwmIdByNameFn(info.first),
|
||||
info.second.m_created, m_getMapObjectByIdFn);
|
||||
campaignData.insert(data.begin(), data.end());
|
||||
}
|
||||
}
|
||||
UpdateFeaturesCache(ReadCampaignFeatures(campaignData));
|
||||
CreateLocalAdsMarks(std::move(campaignData));
|
||||
}
|
||||
|
||||
LocalAdsManager::CampaignData LocalAdsManager::ParseCampaign(std::vector<uint8_t> const & rawData,
|
||||
MwmSet::MwmId const & mwmId,
|
||||
Timestamp timestamp,
|
||||
GetMapObjectByIdFn const & getMapObjectByIdFn) const
|
||||
{
|
||||
ASSERT(getMapObjectByIdFn != nullptr, ());
|
||||
CampaignData data;
|
||||
auto campaigns = local_ads::Deserialize(rawData);
|
||||
for (local_ads::Campaign const & campaign : campaigns)
|
||||
{
|
||||
FeatureID featureId(mwmId, campaign.m_featureId);
|
||||
if (!featureId.IsValid())
|
||||
continue;
|
||||
|
||||
if (campaign.m_priority == kHiddenFeaturePriority)
|
||||
{
|
||||
data.insert(std::make_pair(featureId, nullptr));
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string iconName = campaign.GetIconName();
|
||||
auto const expiration = timestamp + std::chrono::hours(24 * campaign.m_daysBeforeExpired);
|
||||
if (iconName.empty() || local_ads::Clock::now() > expiration)
|
||||
continue;
|
||||
|
||||
auto const mapObject = getMapObjectByIdFn(featureId);
|
||||
if (mapObject.GetID().IsValid())
|
||||
{
|
||||
auto const customIcon = GetCustomIcon(mapObject);
|
||||
if (!customIcon.empty())
|
||||
iconName = customIcon;
|
||||
}
|
||||
|
||||
auto markData = std::make_shared<LocalAdsMarkData>();
|
||||
markData->m_symbolName = iconName;
|
||||
markData->m_minZoomLevel = campaign.m_minZoomLevel;
|
||||
data.insert(std::make_pair(featureId, std::move(markData)));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void LocalAdsManager::CreateLocalAdsMarks(CampaignData && campaignData)
|
||||
{
|
||||
// Here we copy campaign data, because we can create user marks only from UI thread.
|
||||
GetPlatform().RunTask(Platform::Thread::Gui, [this, campaignData = std::move(campaignData)]()
|
||||
{
|
||||
if (m_bmManager == nullptr)
|
||||
return;
|
||||
|
||||
auto editSession = m_bmManager->GetEditSession();
|
||||
for (auto const & data : campaignData)
|
||||
{
|
||||
if (data.second == nullptr)
|
||||
continue;
|
||||
|
||||
auto * mark = editSession.CreateUserMark<LocalAdsMark>(data.second->m_position);
|
||||
mark->SetData(LocalAdsMarkData(*data.second));
|
||||
mark->SetFeatureId(data.first);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void LocalAdsManager::DeleteLocalAdsMarks(MwmSet::MwmId const & mwmId)
|
||||
{
|
||||
GetPlatform().RunTask(Platform::Thread::Gui, [this, mwmId]()
|
||||
{
|
||||
if (m_bmManager == nullptr)
|
||||
return;
|
||||
|
||||
m_bmManager->GetEditSession().DeleteUserMarks<LocalAdsMark>(UserMark::Type::LOCAL_ADS,
|
||||
[&mwmId](LocalAdsMark const * mark)
|
||||
{
|
||||
return mark->GetFeatureID().m_mwmId == mwmId;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void LocalAdsManager::DeleteAllLocalAdsMarks()
|
||||
{
|
||||
GetPlatform().RunTask(Platform::Thread::Gui, [this]()
|
||||
{
|
||||
if (m_bmManager == nullptr)
|
||||
return;
|
||||
|
||||
m_bmManager->GetEditSession().ClearGroup(UserMark::Type::LOCAL_ADS);
|
||||
});
|
||||
}
|
||||
|
||||
LocalAdsManager::FeaturesCache LocalAdsManager::ReadCampaignFeatures(CampaignData & campaignData) const
|
||||
{
|
||||
ASSERT(m_readFeaturesFn != nullptr, ());
|
||||
|
||||
LocalAdsManager::FeaturesCache cache;
|
||||
|
||||
std::vector<FeatureID> features;
|
||||
features.reserve(campaignData.size());
|
||||
for (auto const & data : campaignData)
|
||||
features.push_back(data.first);
|
||||
|
||||
auto const deviceLang = StringUtf8Multilang::GetLangIndex(languages::GetCurrentNorm());
|
||||
m_readFeaturesFn(
|
||||
[&campaignData, &cache, deviceLang](FeatureType & ft) {
|
||||
auto it = campaignData.find(ft.GetID());
|
||||
CHECK(it != campaignData.end(), ());
|
||||
|
||||
LocalAdsManager::CacheEntry entry;
|
||||
entry.m_position = feature::GetCenter(ft, scales::GetUpperScale());
|
||||
entry.m_isVisibleOnMap = it->second != nullptr;
|
||||
if (it->second != nullptr)
|
||||
{
|
||||
it->second->m_position = entry.m_position;
|
||||
ft.GetPreferredNames(true /* allowTranslit */, deviceLang, it->second->m_mainText, it->second->m_auxText);
|
||||
}
|
||||
cache.insert(std::make_pair(it->first, entry));
|
||||
},
|
||||
features);
|
||||
return cache;
|
||||
}
|
||||
|
||||
void LocalAdsManager::UpdateFeaturesCache(FeaturesCache && features)
|
||||
{
|
||||
df::CustomFeatures customFeatures;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_featuresCacheMutex);
|
||||
if (!features.empty())
|
||||
m_featuresCache.insert(features.begin(), features.end());
|
||||
for (auto const & entry : m_featuresCache)
|
||||
customFeatures.insert(std::make_pair(entry.first, entry.second.m_isVisibleOnMap));
|
||||
}
|
||||
m_drapeEngine.SafeCall(&df::DrapeEngine::SetCustomFeatures, std::move(customFeatures));
|
||||
}
|
||||
|
||||
void LocalAdsManager::ClearLocalAdsForMwm(MwmSet::MwmId const & mwmId)
|
||||
{
|
||||
// Clear feature cache.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_featuresCacheMutex);
|
||||
for (auto it = m_featuresCache.begin(); it != m_featuresCache.end();)
|
||||
{
|
||||
if (it->first.m_mwmId == mwmId)
|
||||
it = m_featuresCache.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove custom features in graphics engine.
|
||||
m_drapeEngine.SafeCall(&df::DrapeEngine::RemoveCustomFeatures, mwmId);
|
||||
|
||||
// Delete marks.
|
||||
DeleteLocalAdsMarks(mwmId);
|
||||
}
|
||||
|
||||
bool LocalAdsManager::HasAds(FeatureID const & featureId) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_featuresCacheMutex);
|
||||
return m_featuresCache.find(featureId) != m_featuresCache.cend();
|
||||
}
|
||||
|
||||
bool LocalAdsManager::HasVisualization(FeatureID const & featureId) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_featuresCacheMutex);
|
||||
auto const it = m_featuresCache.find(featureId);
|
||||
if (it == m_featuresCache.cend())
|
||||
return false;
|
||||
|
||||
return it->second.m_isVisibleOnMap;
|
||||
}
|
||||
|
||||
bool LocalAdsManager::IsSupportedType(feature::TypesHolder const & types) const
|
||||
{
|
||||
return m_supportedTypes.Contains(types);
|
||||
}
|
||||
|
||||
std::string LocalAdsManager::GetCompanyUrl(FeatureID const & featureId) const
|
||||
{
|
||||
return MakeCampaignPageURL(featureId);
|
||||
}
|
||||
|
||||
void LocalAdsManager::OnSubscriptionChanged(SubscriptionType type, bool isActive)
|
||||
{
|
||||
if (type != SubscriptionType::RemoveAds)
|
||||
return;
|
||||
|
||||
bool enabled = !isActive;
|
||||
if (m_isEnabled == enabled)
|
||||
return;
|
||||
|
||||
m_isEnabled = enabled;
|
||||
m_lastMwms.clear();
|
||||
if (enabled)
|
||||
{
|
||||
if (!m_isStarted)
|
||||
Start();
|
||||
else
|
||||
Invalidate();
|
||||
}
|
||||
else
|
||||
{
|
||||
GetPlatform().RunTask(Platform::Thread::File, [this]
|
||||
{
|
||||
// Clear campaigns data.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_campaignsMutex);
|
||||
m_campaigns.clear();
|
||||
m_info.clear();
|
||||
m_failedDownloads.clear();
|
||||
m_downloadingMwms.clear();
|
||||
}
|
||||
|
||||
// Clear features cache.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_featuresCacheMutex);
|
||||
m_featuresCache.clear();
|
||||
}
|
||||
|
||||
// Clear all graphics.
|
||||
DeleteAllLocalAdsMarks();
|
||||
m_drapeEngine.SafeCall(&df::DrapeEngine::RemoveAllCustomFeatures);
|
||||
});
|
||||
|
||||
// Disable statistics collection.
|
||||
m_statistics.SetEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalAdsManager::OnLocationUpdate(location::GpsInfo const & info, int zoomLevel)
|
||||
{
|
||||
if (!m_isEnabled)
|
||||
return;
|
||||
|
||||
if (info.m_horizontalAccuracy > kMaxAllowableAccuracyInMeters)
|
||||
return;
|
||||
|
||||
if (std::chrono::steady_clock::now() < (m_lastCheckTime + kMinCheckInterval))
|
||||
return;
|
||||
|
||||
auto const pt = mercator::FromLatLon(info.m_latitude, info.m_longitude);
|
||||
|
||||
if ((m_foundFeatureHitCount == 0 || m_foundFeatureHitCount == kMaxHitCount) &&
|
||||
mercator::DistanceOnEarth(m_lastCheckedPos, pt) < kMinCheckDistanceInMeters)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto const radius = std::max(info.m_horizontalAccuracy, kMinSearchRadiusInMeters);
|
||||
auto searchRect = mercator::RectByCenterXYAndSizeInMeters(pt, radius);
|
||||
|
||||
FeatureID fid;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_featuresCacheMutex);
|
||||
double minDist = std::numeric_limits<double>::max();
|
||||
for (auto const & pair : m_featuresCache)
|
||||
{
|
||||
auto const & pos = pair.second.m_position;
|
||||
if (!searchRect.IsPointInside(pos))
|
||||
continue;
|
||||
auto const dist = mercator::DistanceOnEarth(pos, pt);
|
||||
if (dist < radius && dist < minDist)
|
||||
{
|
||||
minDist = dist;
|
||||
fid = pair.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_lastCheckTime = std::chrono::steady_clock::now();
|
||||
m_lastCheckedPos = pt;
|
||||
|
||||
if (fid.IsValid())
|
||||
{
|
||||
m_statistics.RegisterEvent(local_ads::Event(local_ads::EventType::Visit, fid.m_mwmId.GetInfo()->GetVersion(),
|
||||
fid.m_mwmId.GetInfo()->GetCountryName(), fid.m_index,
|
||||
static_cast<uint8_t>(zoomLevel), local_ads::Clock::now(),
|
||||
info.m_latitude, info.m_longitude,
|
||||
static_cast<uint16_t>(info.m_horizontalAccuracy)));
|
||||
if (m_lastFoundFeature != fid)
|
||||
m_foundFeatureHitCount = 1;
|
||||
else if (m_foundFeatureHitCount < kMaxHitCount)
|
||||
++m_foundFeatureHitCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_foundFeatureHitCount = 0;
|
||||
}
|
||||
m_lastFoundFeature = fid;
|
||||
}
|
||||
|
||||
bool LocalAdsManager::BackoffStats::CanRetry() const
|
||||
{
|
||||
return !m_fileIsAbsent && m_attemptsCount < kMaxDownloadingAttempts &&
|
||||
std::chrono::steady_clock::now() > (m_lastDownloading + m_currentTimeout);
|
||||
}
|
||||
|
||||
namespace lightweight
|
||||
{
|
||||
void LocalAdsFeaturesReader::ReadCampaignFeaturesFile()
|
||||
{
|
||||
m_features.clear();
|
||||
std::string const featuresFile = GetPath(kCampaignFeaturesFile);
|
||||
try
|
||||
{
|
||||
FileReader reader(featuresFile);
|
||||
ReaderSource<FileReader> src(reader);
|
||||
|
||||
auto const version = features_cache::DeserializeVersion(src);
|
||||
if (version != features_cache::Version::V0)
|
||||
return;
|
||||
|
||||
std::string countryId;
|
||||
int64_t mwmVersion;
|
||||
std::vector<features_cache::PackedCampaignFeature> packedData;
|
||||
while (src.Size() > 0)
|
||||
{
|
||||
features_cache::DeserializeMwmData(src, countryId, mwmVersion);
|
||||
features_cache::DeserializePackedFeatures(src, packedData);
|
||||
|
||||
for (auto const & data : packedData)
|
||||
{
|
||||
auto const pos = Int64ToPointObsolete(data.m_mercator, kPointCoordBits);
|
||||
m_features.push_back(CampaignFeature(mwmVersion, countryId, data.m_featureIndex,
|
||||
mercator::YToLat(pos.y), mercator::XToLon(pos.x)));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Reader::Exception const & ex)
|
||||
{
|
||||
LOG(LWARNING, ("Error reading file:", featuresFile, ex.Msg()));
|
||||
FileWriter::DeleteFileX(featuresFile);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CampaignFeature> LocalAdsFeaturesReader::GetCampaignFeatures(double lat, double lon, double radiusInMeters,
|
||||
uint32_t maxCount)
|
||||
{
|
||||
if (!m_initialized)
|
||||
{
|
||||
ReadCampaignFeaturesFile();
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
if (m_features.empty())
|
||||
return {};
|
||||
|
||||
auto const pt = mercator::FromLatLon(lat, lon);
|
||||
auto searchRect = mercator::RectByCenterXYAndSizeInMeters(pt, radiusInMeters);
|
||||
|
||||
std::multimap<uint32_t, CampaignFeature> sortedFeatures;
|
||||
for (auto const & f : m_features)
|
||||
{
|
||||
auto const pos = mercator::FromLatLon(f.m_lat, f.m_lon);
|
||||
if (!searchRect.IsPointInside(pos))
|
||||
continue;
|
||||
auto const dist = static_cast<uint32_t>(mercator::DistanceOnEarth(pos, pt));
|
||||
sortedFeatures.insert(std::make_pair(dist, f));
|
||||
}
|
||||
|
||||
std::vector<CampaignFeature> filteredFeatures;
|
||||
filteredFeatures.reserve(std::min(sortedFeatures.size(), static_cast<size_t>(maxCount)));
|
||||
for (auto const & pair : sortedFeatures)
|
||||
{
|
||||
filteredFeatures.push_back(pair.second);
|
||||
if (filteredFeatures.size() == maxCount)
|
||||
break;
|
||||
}
|
||||
return filteredFeatures;
|
||||
}
|
||||
|
||||
void Statistics::RegisterEvent(local_ads::Event && event)
|
||||
{
|
||||
m_statistics.RegisterEventSync(std::move(event));
|
||||
}
|
||||
} // namespace lightweight
|
|
@ -1,216 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "map/purchase.hpp"
|
||||
|
||||
#include "local_ads/statistics.hpp"
|
||||
|
||||
#include "drape_frontend/custom_features_context.hpp"
|
||||
#include "drape_frontend/drape_engine_safe_ptr.hpp"
|
||||
|
||||
#include "drape/pointers.hpp"
|
||||
|
||||
#include "geometry/rect2d.hpp"
|
||||
#include "geometry/screenbase.hpp"
|
||||
|
||||
#include "indexer/feature.hpp"
|
||||
#include "indexer/ftypes_mapping.hpp"
|
||||
#include "indexer/map_object.hpp"
|
||||
#include "indexer/mwm_set.hpp"
|
||||
|
||||
#include "platform/location.hpp"
|
||||
|
||||
#include "base/thread.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace feature
|
||||
{
|
||||
class TypesHolder;
|
||||
}
|
||||
|
||||
class BookmarkManager;
|
||||
struct LocalAdsMarkData;
|
||||
|
||||
struct CampaignFeature
|
||||
{
|
||||
CampaignFeature() = default;
|
||||
CampaignFeature(int64_t const & mwmVersion, std::string const & countryId, uint32_t featureIndex,
|
||||
double lat, double lon)
|
||||
: m_mwmVersion(mwmVersion), m_countryId(countryId), m_featureIndex(featureIndex), m_lat(lat), m_lon(lon) {}
|
||||
|
||||
int64_t m_mwmVersion = FeatureID::kInvalidMwmVersion;
|
||||
std::string m_countryId;
|
||||
uint32_t m_featureIndex = 0;
|
||||
double m_lat = 0.0;
|
||||
double m_lon = 0.0;
|
||||
};
|
||||
|
||||
class LocalAdsManager : public SubscriptionListener
|
||||
{
|
||||
public:
|
||||
using GetMwmsByRectFn = std::function<std::vector<MwmSet::MwmId>(m2::RectD const &)>;
|
||||
using GetMwmIdByNameFn = std::function<MwmSet::MwmId(std::string const &)>;
|
||||
using ReadFeatureTypeFn = std::function<void(FeatureType &)>;
|
||||
using ReadFeaturesFn = std::function<void(ReadFeatureTypeFn const &,
|
||||
std::vector<FeatureID> const & features)>;
|
||||
using GetMapObjectByIdFn = std::function<osm::MapObject(FeatureID const &)>;
|
||||
using OnCampaignFeaturesFn = std::function<void(std::vector<FeatureID> const & features)>;
|
||||
using Timestamp = local_ads::Timestamp;
|
||||
|
||||
LocalAdsManager(GetMwmsByRectFn && getMwmsByRectFn, GetMwmIdByNameFn && getMwmIdByName,
|
||||
ReadFeaturesFn && readFeaturesFn, GetMapObjectByIdFn && getMapObjectByIdFn);
|
||||
|
||||
void Startup(BookmarkManager * bmManager, bool isEnabled);
|
||||
void SetDrapeEngine(ref_ptr<df::DrapeEngine> engine);
|
||||
void UpdateViewport(ScreenBase const & screen);
|
||||
|
||||
void OnDownloadCountry(std::string const & countryName);
|
||||
void OnMwmDeregistered(platform::LocalCountryFile const & countryFile);
|
||||
|
||||
void Invalidate();
|
||||
|
||||
local_ads::Statistics & GetStatistics() { return m_statistics; }
|
||||
local_ads::Statistics const & GetStatistics() const { return m_statistics; }
|
||||
|
||||
bool HasAds(FeatureID const & featureId) const;
|
||||
bool HasVisualization(FeatureID const & featureId) const;
|
||||
bool IsSupportedType(feature::TypesHolder const & types) const;
|
||||
|
||||
std::string GetCompanyUrl(FeatureID const & featureId) const;
|
||||
|
||||
void OnSubscriptionChanged(SubscriptionType type, bool isActive) override;
|
||||
void OnLocationUpdate(location::GpsInfo const & info, int zoomLevel);
|
||||
|
||||
private:
|
||||
enum class RequestType
|
||||
{
|
||||
Download,
|
||||
Delete
|
||||
};
|
||||
using Request = std::pair<MwmSet::MwmId, RequestType>;
|
||||
|
||||
struct CacheEntry
|
||||
{
|
||||
bool m_isVisibleOnMap = false;
|
||||
m2::PointD m_position;
|
||||
};
|
||||
using FeaturesCache = std::map<FeatureID, CacheEntry>;
|
||||
|
||||
void Start();
|
||||
|
||||
void InvalidateImpl();
|
||||
|
||||
void ProcessRequests(std::set<Request> && campaignMwms);
|
||||
|
||||
void ReadCampaignFile(std::string const & campaignFile);
|
||||
void WriteCampaignFile(std::string const & campaignFile);
|
||||
|
||||
void WriteFeaturesFile(std::string const & featuresFile);
|
||||
|
||||
using CampaignData = std::map<FeatureID, std::shared_ptr<LocalAdsMarkData>>;
|
||||
CampaignData ParseCampaign(std::vector<uint8_t> const & rawData, MwmSet::MwmId const & mwmId,
|
||||
Timestamp timestamp, GetMapObjectByIdFn const & getMapObjectByIdFn) const;
|
||||
FeaturesCache ReadCampaignFeatures(CampaignData & campaignData) const;
|
||||
|
||||
void CreateLocalAdsMarks(CampaignData && campaignData);
|
||||
void DeleteLocalAdsMarks(MwmSet::MwmId const & mwmId);
|
||||
void DeleteAllLocalAdsMarks();
|
||||
|
||||
void UpdateFeaturesCache(FeaturesCache && features);
|
||||
void ClearLocalAdsForMwm(MwmSet::MwmId const &mwmId);
|
||||
|
||||
void FillSupportedTypes();
|
||||
|
||||
// Returned value means if downloading process finished correctly or was interrupted
|
||||
// by some reason.
|
||||
bool DownloadCampaign(MwmSet::MwmId const & mwmId, std::vector<uint8_t> & bytes);
|
||||
|
||||
void RequestCampaigns(std::vector<MwmSet::MwmId> const & mwmIds);
|
||||
|
||||
GetMwmsByRectFn const m_getMwmsByRectFn;
|
||||
GetMwmIdByNameFn const m_getMwmIdByNameFn;
|
||||
ReadFeaturesFn const m_readFeaturesFn;
|
||||
GetMapObjectByIdFn const m_getMapObjectByIdFn;
|
||||
|
||||
std::atomic<bool> m_isStarted;
|
||||
|
||||
BookmarkManager * m_bmManager;
|
||||
|
||||
df::DrapeEngineSafePtr m_drapeEngine;
|
||||
|
||||
std::map<std::string, bool> m_campaigns;
|
||||
struct CampaignInfo
|
||||
{
|
||||
Timestamp m_created;
|
||||
std::vector<uint8_t> m_data;
|
||||
};
|
||||
std::map<std::string, CampaignInfo> m_info;
|
||||
std::mutex m_campaignsMutex;
|
||||
|
||||
FeaturesCache m_featuresCache;
|
||||
mutable std::mutex m_featuresCacheMutex;
|
||||
|
||||
ftypes::HashSetMatcher<uint32_t> m_supportedTypes;
|
||||
|
||||
struct BackoffStats
|
||||
{
|
||||
BackoffStats() = default;
|
||||
BackoffStats(std::chrono::steady_clock::time_point lastDownloading,
|
||||
std::chrono::seconds currentTimeout,
|
||||
uint8_t attemptsCount, bool fileIsAbsent)
|
||||
: m_lastDownloading(lastDownloading)
|
||||
, m_currentTimeout(currentTimeout)
|
||||
, m_attemptsCount(attemptsCount)
|
||||
, m_fileIsAbsent(fileIsAbsent)
|
||||
{}
|
||||
|
||||
std::chrono::steady_clock::time_point m_lastDownloading = {};
|
||||
std::chrono::seconds m_currentTimeout = std::chrono::seconds(0);
|
||||
uint8_t m_attemptsCount = 0;
|
||||
bool m_fileIsAbsent = false;
|
||||
|
||||
bool CanRetry() const;
|
||||
};
|
||||
std::map<MwmSet::MwmId, BackoffStats> m_failedDownloads;
|
||||
std::set<MwmSet::MwmId> m_downloadingMwms;
|
||||
std::vector<MwmSet::MwmId> m_lastMwms;
|
||||
|
||||
local_ads::Statistics m_statistics;
|
||||
std::atomic<bool> m_isEnabled = false;
|
||||
|
||||
m2::PointD m_lastCheckedPos;
|
||||
std::chrono::steady_clock::time_point m_lastCheckTime;
|
||||
FeatureID m_lastFoundFeature;
|
||||
uint32_t m_foundFeatureHitCount = 0;
|
||||
};
|
||||
|
||||
namespace lightweight
|
||||
{
|
||||
class LocalAdsFeaturesReader
|
||||
{
|
||||
public:
|
||||
std::vector<CampaignFeature> GetCampaignFeatures(double lat, double lon,
|
||||
double radiusInMeters, uint32_t maxCount);
|
||||
private:
|
||||
void ReadCampaignFeaturesFile();
|
||||
|
||||
bool m_initialized = false;
|
||||
std::vector<CampaignFeature> m_features;
|
||||
};
|
||||
|
||||
class Statistics
|
||||
{
|
||||
public:
|
||||
void RegisterEvent(local_ads::Event && event);
|
||||
|
||||
private:
|
||||
local_ads::Statistics m_statistics;
|
||||
};
|
||||
} // namespace lightweight
|
|
@ -1,67 +0,0 @@
|
|||
#include "map/local_ads_mark.hpp"
|
||||
|
||||
#include "drape_frontend/color_constants.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace
|
||||
{
|
||||
static std::string const kLocalAdsPrimaryText = "LocalAdsPrimaryText";
|
||||
static std::string const kLocalAdsPrimaryTextOutline = "LocalAdsPrimaryTextOutline";
|
||||
static std::string const kLocalAdsSecondaryText = "LocalAdsSecondaryText";
|
||||
static std::string const kLocalAdsSecondaryTextOutline = "LocalAdsSecondaryTextOutline";
|
||||
|
||||
float const kLocalAdsPrimaryTextSize = 11.0f;
|
||||
float const kLocalAdsSecondaryTextSize = 10.0f;
|
||||
float const kSecondaryOffsetY = 2.0;
|
||||
} // namespace
|
||||
|
||||
LocalAdsMark::LocalAdsMark(m2::PointD const & ptOrg)
|
||||
: UserMark(ptOrg, Type::LOCAL_ADS)
|
||||
{
|
||||
m_titleDecl.m_anchor = dp::Top;
|
||||
m_titleDecl.m_primaryTextFont.m_color = df::GetColorConstant(kLocalAdsPrimaryText);
|
||||
m_titleDecl.m_primaryTextFont.m_outlineColor = df::GetColorConstant(kLocalAdsPrimaryTextOutline);
|
||||
m_titleDecl.m_primaryTextFont.m_size = kLocalAdsPrimaryTextSize;
|
||||
m_titleDecl.m_secondaryTextFont.m_color = df::GetColorConstant(kLocalAdsSecondaryText);
|
||||
m_titleDecl.m_secondaryTextFont.m_outlineColor = df::GetColorConstant(kLocalAdsSecondaryTextOutline);
|
||||
m_titleDecl.m_secondaryTextFont.m_size = kLocalAdsSecondaryTextSize;
|
||||
m_titleDecl.m_secondaryOffset = m2::PointF(0, kSecondaryOffsetY);
|
||||
}
|
||||
|
||||
drape_ptr<df::UserPointMark::SymbolNameZoomInfo> LocalAdsMark::GetSymbolNames() const
|
||||
{
|
||||
auto symbol = make_unique_dp<SymbolNameZoomInfo>();
|
||||
symbol->insert(std::make_pair(1 /* zoomLevel */, m_data.m_symbolName));
|
||||
return symbol;
|
||||
}
|
||||
|
||||
df::DepthLayer LocalAdsMark::GetDepthLayer() const
|
||||
{
|
||||
return df::DepthLayer::LocalAdsMarkLayer;
|
||||
}
|
||||
|
||||
drape_ptr<df::UserPointMark::TitlesInfo> LocalAdsMark::GetTitleDecl() const
|
||||
{
|
||||
auto titles = make_unique_dp<TitlesInfo>();
|
||||
titles->push_back(m_titleDecl);
|
||||
return titles;
|
||||
}
|
||||
|
||||
void LocalAdsMark::SetData(LocalAdsMarkData && data)
|
||||
{
|
||||
SetDirty();
|
||||
m_data = std::move(data);
|
||||
|
||||
m_titleDecl.m_primaryText = m_data.m_mainText;
|
||||
if (!m_titleDecl.m_primaryText.empty())
|
||||
m_titleDecl.m_secondaryText = m_data.m_auxText;
|
||||
else
|
||||
m_titleDecl.m_secondaryText.clear();
|
||||
}
|
||||
|
||||
void LocalAdsMark::SetFeatureId(FeatureID const & id)
|
||||
{
|
||||
SetDirty();
|
||||
m_featureId = id;
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "map/user_mark_layer.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
struct LocalAdsMarkData
|
||||
{
|
||||
m2::PointD m_position = m2::PointD::Zero();
|
||||
std::string m_symbolName;
|
||||
uint8_t m_minZoomLevel = 1;
|
||||
std::string m_mainText;
|
||||
std::string m_auxText;
|
||||
uint16_t m_priority = std::numeric_limits<uint16_t>::max();
|
||||
};
|
||||
|
||||
class LocalAdsMark : public UserMark
|
||||
{
|
||||
public:
|
||||
explicit LocalAdsMark(m2::PointD const & ptOrg);
|
||||
|
||||
df::DepthLayer GetDepthLayer() const override;
|
||||
|
||||
drape_ptr<SymbolNameZoomInfo> GetSymbolNames() const override;
|
||||
|
||||
drape_ptr<TitlesInfo> GetTitleDecl() const override;
|
||||
uint16_t GetPriority() const override { return m_data.m_priority; }
|
||||
bool SymbolIsPOI() const override { return true; }
|
||||
bool HasTitlePriority() const override { return true; }
|
||||
int GetMinZoom() const override { return static_cast<int>(m_data.m_minZoomLevel); }
|
||||
FeatureID GetFeatureID() const override { return m_featureId; }
|
||||
|
||||
void SetData(LocalAdsMarkData && data);
|
||||
void SetFeatureId(FeatureID const & id);
|
||||
|
||||
private:
|
||||
LocalAdsMarkData m_data;
|
||||
FeatureID m_featureId;
|
||||
dp::TitleDecl m_titleDecl;
|
||||
};
|
|
@ -1,96 +0,0 @@
|
|||
#include "map/local_ads_manager.hpp"
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
void LocalAdsManager::FillSupportedTypes()
|
||||
{
|
||||
m_supportedTypes.Append<std::initializer_list<std::initializer_list<char const *>>>(
|
||||
{{"amenity", "atm"},
|
||||
{"amenity", "bank"},
|
||||
{"amenity", "bar"},
|
||||
{"amenity", "bbq"},
|
||||
{"amenity", "bicycle_parking"},
|
||||
{"amenity", "bicycle_rental"},
|
||||
{"amenity", "brothel"},
|
||||
{"amenity", "bureau_de_change"},
|
||||
{"amenity", "cafe"},
|
||||
{"amenity", "car_rental"},
|
||||
{"amenity", "car_sharing"},
|
||||
{"amenity", "car_wash"},
|
||||
{"amenity", "casino"},
|
||||
{"amenity", "charging_station"},
|
||||
{"amenity", "childcare"},
|
||||
{"amenity", "cinema"},
|
||||
{"amenity", "clinic"},
|
||||
{"amenity", "college"},
|
||||
{"amenity", "community_centre"},
|
||||
{"amenity", "courthouse"},
|
||||
{"amenity", "dentist"},
|
||||
{"amenity", "doctors"},
|
||||
{"amenity", "fast_food"},
|
||||
{"amenity", "fuel"},
|
||||
{"amenity", "grave_yard"},
|
||||
{"amenity", "hospital"},
|
||||
{"amenity", "hunting_stand"},
|
||||
{"amenity", "kindergarten"},
|
||||
{"amenity", "library"},
|
||||
{"amenity", "marketplace"},
|
||||
{"amenity", "nightclub"},
|
||||
{"amenity", "parking"},
|
||||
{"amenity", "pharmacy"},
|
||||
{"amenity", "post_office"},
|
||||
{"amenity", "pub"},
|
||||
{"amenity", "public_bookcase"},
|
||||
{"amenity", "recycling"},
|
||||
{"amenity", "restaurant"},
|
||||
{"amenity", "school"},
|
||||
{"amenity", "shelter"},
|
||||
{"amenity", "taxi"},
|
||||
{"amenity", "telephone"},
|
||||
{"amenity", "theatre"},
|
||||
{"amenity", "toilets"},
|
||||
{"amenity", "townhall"},
|
||||
{"amenity", "university"},
|
||||
{"amenity", "vending_machine"},
|
||||
{"amenity", "veterinary"},
|
||||
{"amenity", "waste_disposal"},
|
||||
|
||||
{"craft"},
|
||||
|
||||
{"historic", "castle"},
|
||||
{"historic", "museum"},
|
||||
{"historic", "ship"},
|
||||
|
||||
{"leisure", "fitness_centre"},
|
||||
{"leisure", "sauna"},
|
||||
{"leisure", "sports_centre"},
|
||||
|
||||
{"man_made", "lighthouse"},
|
||||
|
||||
{"office"},
|
||||
|
||||
{"shop"},
|
||||
|
||||
{"sponsored", "booking"},
|
||||
|
||||
{"sport"},
|
||||
|
||||
{"tourism", "alpine_hut"},
|
||||
{"tourism", "apartment"},
|
||||
{"tourism", "artwork"},
|
||||
{"tourism", "attraction"},
|
||||
{"tourism", "camp_site"},
|
||||
{"tourism", "caravan_site"},
|
||||
{"tourism", "chalet"},
|
||||
{"tourism", "gallery"},
|
||||
{"tourism", "guest_house"},
|
||||
{"tourism", "hostel"},
|
||||
{"tourism", "hotel"},
|
||||
{"tourism", "information", "office"},
|
||||
{"tourism", "motel"},
|
||||
{"tourism", "museum"},
|
||||
{"tourism", "picnic_site"},
|
||||
{"tourism", "resort"},
|
||||
{"tourism", "viewpoint"},
|
||||
{"tourism", "zoo"}});
|
||||
}
|
|
@ -57,7 +57,6 @@ omim_link_libraries(
|
|||
ugc
|
||||
partners_api
|
||||
web_api
|
||||
local_ads
|
||||
kml
|
||||
editor
|
||||
indexer
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace
|
|||
{
|
||||
using Runner = Platform::ThreadRunner;
|
||||
|
||||
static FrameworkParams const kFrameworkParams(false /* m_enableLocalAds */, false /* m_enableDiffs */);
|
||||
static FrameworkParams const kFrameworkParams(false /* m_enableDiffs */);
|
||||
|
||||
char const * kmlString =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
||||
|
|
|
@ -30,7 +30,7 @@ using namespace std;
|
|||
|
||||
UNIT_TEST(CountriesNamesTest)
|
||||
{
|
||||
Framework f(FrameworkParams(false /* m_enableLocalAds */, false /* m_enableDiffs */));
|
||||
Framework f(FrameworkParams(false /* m_enableDiffs */));
|
||||
auto & storage = f.GetStorage();
|
||||
auto const & synonyms = storage.GetCountryNameSynonyms();
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ using namespace std;
|
|||
|
||||
UNIT_TEST(Framework_ForEachFeatureAtPoint_And_Others)
|
||||
{
|
||||
Framework frm(FrameworkParams(false /* m_enableLocalAds */, false /* m_enableDiffs */));
|
||||
Framework frm(FrameworkParams(false /* m_enableDiffs */));
|
||||
frm.DeregisterAllMaps();
|
||||
frm.RegisterMap(platform::LocalCountryFile::MakeForTesting("minsk-pass"));
|
||||
|
||||
|
|
|
@ -26,15 +26,6 @@ struct LightFrameworkTest
|
|||
TEST_EQUAL(f.GetNumberOfUnsentUGC(), 0, ());
|
||||
TEST(!f.IsUserAuthenticated(), ());
|
||||
}
|
||||
|
||||
{
|
||||
Framework f(REQUEST_TYPE_LOCAL_ADS_FEATURES | REQUEST_TYPE_LOCAL_ADS_STATISTICS);
|
||||
auto const features = f.GetLocalAdsFeatures(0.0 /* lat */, 0.0 /* lon */,
|
||||
100 /* radiusInMeters */, 0 /* maxCount */);
|
||||
auto stats = f.GetLocalAdsStatistics();
|
||||
TEST(stats != nullptr, ());
|
||||
TEST_EQUAL(features.size(), 0, ());
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace lightweight
|
||||
|
|
|
@ -26,7 +26,7 @@ using namespace url_scheme;
|
|||
namespace
|
||||
{
|
||||
using UrlType = ParsedMapApi::UrlType;
|
||||
static FrameworkParams const kFrameworkParams(false /* m_enableLocalAds */, false /* m_enableDiffs */);
|
||||
static FrameworkParams const kFrameworkParams(false /* m_enableDiffs */);
|
||||
|
||||
void ToMercatoToLatLon(double & lat, double & lon)
|
||||
{
|
||||
|
|
|
@ -273,10 +273,8 @@ UNIT_TEST(PowerManager_OnBatteryLevelChanged)
|
|||
TEST_EQUAL(subscriber.m_onFacilityEvents[3].m_state, false, ());
|
||||
TEST_EQUAL(subscriber.m_onFacilityEvents[4].m_facility, Facility::UgcUploading, ());
|
||||
TEST_EQUAL(subscriber.m_onFacilityEvents[4].m_state, false, ());
|
||||
TEST_EQUAL(subscriber.m_onFacilityEvents[5].m_facility, Facility::LocalAdsDataDownloading, ());
|
||||
TEST_EQUAL(subscriber.m_onFacilityEvents[5].m_facility, Facility::AdsDownloading, ());
|
||||
TEST_EQUAL(subscriber.m_onFacilityEvents[5].m_state, false, ());
|
||||
TEST_EQUAL(subscriber.m_onFacilityEvents[6].m_facility, Facility::AdsDownloading, ());
|
||||
TEST_EQUAL(subscriber.m_onFacilityEvents[6].m_state, false, ());
|
||||
|
||||
subscriber.m_onShemeEvents.clear();
|
||||
subscriber.m_onFacilityEvents.clear();
|
||||
|
|
|
@ -53,14 +53,6 @@ enum class SponsoredType
|
|||
PromoCatalogOutdoor,
|
||||
};
|
||||
|
||||
enum class LocalAdsStatus
|
||||
{
|
||||
NotAvailable,
|
||||
Candidate,
|
||||
Customer,
|
||||
Hidden
|
||||
};
|
||||
|
||||
enum class LocalsStatus
|
||||
{
|
||||
NotAvailable,
|
||||
|
@ -274,13 +266,6 @@ public:
|
|||
void SetLocalsPageUrl(std::string const & url) { m_localsUrl = url; }
|
||||
std::string const & GetLocalsPageUrl() const { return m_localsUrl; }
|
||||
|
||||
/// Local ads
|
||||
void SetLocalAdsStatus(LocalAdsStatus status) { m_localAdsStatus = status; }
|
||||
LocalAdsStatus GetLocalAdsStatus() const { return m_localAdsStatus; }
|
||||
void SetLocalAdsUrl(std::string const & url) { m_localAdsUrl = url; }
|
||||
std::string const & GetLocalAdsUrl() const { return m_localAdsUrl; }
|
||||
void SetAdsEngine(ads::Engine * const engine) { m_adsEngine = engine; }
|
||||
|
||||
/// Routing
|
||||
void SetRouteMarkType(RouteMarkType type) { m_routeMarkType = type; }
|
||||
RouteMarkType GetRouteMarkType() const { return m_routeMarkType; }
|
||||
|
@ -393,8 +378,6 @@ private:
|
|||
|
||||
/// Ads
|
||||
std::vector<taxi::Provider::Type> m_reachableByProviders;
|
||||
std::string m_localAdsUrl;
|
||||
LocalAdsStatus m_localAdsStatus = LocalAdsStatus::NotAvailable;
|
||||
/// Ads source.
|
||||
ads::Engine * m_adsEngine = nullptr;
|
||||
/// Sponsored type or None.
|
||||
|
|
|
@ -15,7 +15,7 @@ std::unordered_map<Scheme, FacilitiesState> const kSchemeToState =
|
|||
{{
|
||||
/* Buildings3d */ true, /* PerspectiveView */ true, /* TrackRecording */ true,
|
||||
/* TrafficJams */ true, /* GpsTrackingForTraffic */ true, /* OsmEditsUploading */ true,
|
||||
/* UgcUploading */ true, /* BookmarkCloudUploading */ true, /* LocalAdsDataDownloading */ true,
|
||||
/* UgcUploading */ true, /* BookmarkCloudUploading */ true,
|
||||
/* MapDownloader */ true, /* StatisticsUploading */ true, /* AdsDownloading */ true
|
||||
}}
|
||||
},
|
||||
|
@ -24,7 +24,7 @@ std::unordered_map<Scheme, FacilitiesState> const kSchemeToState =
|
|||
{{
|
||||
/* Buildings3d */ true, /* PerspectiveView */ false, /* TrackRecording */ true,
|
||||
/* TrafficJams */ true, /* GpsTrackingForTraffic */ true, /* OsmEditsUploading */ true,
|
||||
/* UgcUploading */ true, /* BookmarkCloudUploading */ false, /* LocalAdsDataDownloading */ true,
|
||||
/* UgcUploading */ true, /* BookmarkCloudUploading */ false,
|
||||
/* MapDownloader */ true, /* StatisticsUploading */ true, /* AdsDownloading */ true
|
||||
}}
|
||||
},
|
||||
|
@ -33,7 +33,7 @@ std::unordered_map<Scheme, FacilitiesState> const kSchemeToState =
|
|||
{{
|
||||
/* Buildings3d */ false, /* PerspectiveView */ false, /* TrackRecording */ false,
|
||||
/* TrafficJams */ false, /* GpsTrackingForTraffic */ false, /* OsmEditsUploading */ false,
|
||||
/* UgcUploading */ false, /* BookmarkCloudUploading */ false, /* LocalAdsDataDownloading */ true,
|
||||
/* UgcUploading */ false, /* BookmarkCloudUploading */ false,
|
||||
/* MapDownloader */ true, /* StatisticsUploading */ true, /* AdsDownloading */ true
|
||||
}}
|
||||
},
|
||||
|
@ -46,7 +46,7 @@ std::unordered_map<AutoScheme, FacilitiesState> const kAutoSchemeToState =
|
|||
{{
|
||||
/* Buildings3d */ true, /* PerspectiveView */ true, /* TrackRecording */ true,
|
||||
/* TrafficJams */ true, /* GpsTrackingForTraffic */ true, /* OsmEditsUploading */ true,
|
||||
/* UgcUploading */ true, /* BookmarkCloudUploading */ true, /* LocalAdsDataDownloading */ true,
|
||||
/* UgcUploading */ true, /* BookmarkCloudUploading */ true,
|
||||
/* MapDownloader */ true, /* StatisticsUploading */ true, /* AdsDownloading */ true
|
||||
}}
|
||||
},
|
||||
|
@ -55,7 +55,7 @@ std::unordered_map<AutoScheme, FacilitiesState> const kAutoSchemeToState =
|
|||
{{
|
||||
/* Buildings3d */ true, /* PerspectiveView */ false, /* TrackRecording */ true,
|
||||
/* TrafficJams */ true, /* GpsTrackingForTraffic */ false, /* OsmEditsUploading */ true,
|
||||
/* UgcUploading */ true, /* BookmarkCloudUploading */ false, /* LocalAdsDataDownloading */ true,
|
||||
/* UgcUploading */ true, /* BookmarkCloudUploading */ false,
|
||||
/* MapDownloader */ false, /* StatisticsUploading */ false, /* AdsDownloading */ true
|
||||
}}
|
||||
},
|
||||
|
@ -64,7 +64,7 @@ std::unordered_map<AutoScheme, FacilitiesState> const kAutoSchemeToState =
|
|||
{{
|
||||
/* Buildings3d */ false, /* PerspectiveView */ false, /* TrackRecording */ false,
|
||||
/* TrafficJams */ false, /* GpsTrackingForTraffic */ false, /* OsmEditsUploading */ false,
|
||||
/* UgcUploading */ false, /* BookmarkCloudUploading */ false, /* LocalAdsDataDownloading */ false,
|
||||
/* UgcUploading */ false, /* BookmarkCloudUploading */ false,
|
||||
/* MapDownloader */ false, /* StatisticsUploading */ false, /* AdsDownloading */ false
|
||||
}}
|
||||
},
|
||||
|
@ -98,7 +98,6 @@ std::string DebugPrint(Facility const facility)
|
|||
case Facility::OsmEditsUploading: return "OsmEditsUploading";
|
||||
case Facility::UgcUploading: return "UgcUploading";
|
||||
case Facility::BookmarkCloudUploading: return "BookmarkCloudUploading";
|
||||
case Facility::LocalAdsDataDownloading: return "LocalAdsDataDownloading";
|
||||
case Facility::MapDownloader: return "MapDownloader";
|
||||
case Facility::StatisticsUploading: return "StatisticsUploading";
|
||||
case Facility::AdsDownloading: return "AdsDownloading";
|
||||
|
|
|
@ -18,7 +18,6 @@ enum class Facility : uint8_t
|
|||
OsmEditsUploading,
|
||||
UgcUploading,
|
||||
BookmarkCloudUploading,
|
||||
LocalAdsDataDownloading,
|
||||
MapDownloader,
|
||||
StatisticsUploading,
|
||||
AdsDownloading,
|
||||
|
|
|
@ -82,7 +82,6 @@ std::string const kPriceChipDiscount = "price-chips-discount";
|
|||
std::string const kPriceChipSelectedDiscount = "price-chips-selected-discount";
|
||||
|
||||
std::string const kRatedDefaultSearchIcon = "rated-default-search-result";
|
||||
std::string const kRatedDefaultSearchIconAds = "local_ads_rated-default-search-result";
|
||||
|
||||
std::string const kBookingNoRatingSearchIcon = "norating-default-l";
|
||||
std::string const & kOsmHotelSearchIcon = kBookingNoRatingSearchIcon;
|
||||
|
@ -136,30 +135,23 @@ int constexpr kUGCBadgeMinZoomLevel = scales::GetUpperCountryScale();
|
|||
int constexpr kGoodRatingZoomLevel = kWorldZoomLevel;
|
||||
int constexpr kBadRatingZoomLevel = scales::GetUpperComfortScale();
|
||||
|
||||
std::string GetSymbol(SearchMarkType searchMarkType, bool hasLocalAds, bool hasRating)
|
||||
std::string GetSymbol(SearchMarkType searchMarkType, bool hasRating)
|
||||
{
|
||||
if (searchMarkType == SearchMarkType::Default && hasRating)
|
||||
return hasLocalAds ? kRatedDefaultSearchIconAds : kRatedDefaultSearchIcon;
|
||||
return kRatedDefaultSearchIcon;
|
||||
|
||||
if (searchMarkType == SearchMarkType::Booking && !hasRating)
|
||||
return kBookingNoRatingSearchIcon;
|
||||
|
||||
if (searchMarkType == SearchMarkType::Hotel && !hasLocalAds)
|
||||
if (searchMarkType == SearchMarkType::Hotel)
|
||||
return kOsmHotelSearchIcon;
|
||||
|
||||
auto const index = static_cast<size_t>(searchMarkType);
|
||||
ASSERT_LESS(index, kSymbols.size(), ());
|
||||
if (hasLocalAds)
|
||||
return "local_ads_" + kSymbols[index];
|
||||
|
||||
return kSymbols[index];
|
||||
}
|
||||
|
||||
bool HasLocalAdsVariant(SearchMarkType searchMarkType)
|
||||
{
|
||||
return searchMarkType != SearchMarkType::NotFound;
|
||||
}
|
||||
|
||||
class SearchMarkTypeChecker
|
||||
{
|
||||
public:
|
||||
|
@ -394,8 +386,6 @@ df::ColorConstant SearchMarkPoint::GetColorConstant() const
|
|||
return m_isSelected ? "SearchmarkSelectedNotAvailable" : "SearchmarkNotAvailable";
|
||||
if (m_isPreparing)
|
||||
return "SearchmarkPreparing";
|
||||
if (m_hasLocalAds)
|
||||
return "RatingGood";
|
||||
if (!HasRating())
|
||||
return "RatingNone";
|
||||
if (!HasGoodRating())
|
||||
|
@ -526,27 +516,23 @@ void SearchMarkPoint::SetMatchedName(std::string const & name)
|
|||
SetAttributeValue(m_matchedName, name);
|
||||
}
|
||||
|
||||
void SearchMarkPoint::SetFromType(uint32_t type, bool hasLocalAds)
|
||||
void SearchMarkPoint::SetFromType(uint32_t type)
|
||||
{
|
||||
SetAttributeValue(m_hasLocalAds, hasLocalAds);
|
||||
SetAttributeValue(m_type, GetSearchMarkType(type));
|
||||
}
|
||||
|
||||
void SearchMarkPoint::SetBookingType(bool hasLocalAds)
|
||||
void SearchMarkPoint::SetBookingType()
|
||||
{
|
||||
SetAttributeValue(m_hasLocalAds, hasLocalAds);
|
||||
SetAttributeValue(m_type, SearchMarkType::Booking);
|
||||
}
|
||||
|
||||
void SearchMarkPoint::SetHotelType(bool hasLocalAds)
|
||||
void SearchMarkPoint::SetHotelType()
|
||||
{
|
||||
SetAttributeValue(m_hasLocalAds, hasLocalAds);
|
||||
SetAttributeValue(m_type, SearchMarkType::Hotel);
|
||||
}
|
||||
|
||||
void SearchMarkPoint::SetNotFoundType()
|
||||
{
|
||||
SetAttributeValue(m_hasLocalAds, false);
|
||||
SetAttributeValue(m_type, SearchMarkType::NotFound);
|
||||
}
|
||||
|
||||
|
@ -624,7 +610,7 @@ std::string SearchMarkPoint::GetSymbolName() const
|
|||
if (m_type >= SearchMarkType::Count)
|
||||
{
|
||||
ASSERT(false, ("Unknown search mark symbol."));
|
||||
symbolName = GetSymbol(SearchMarkType::Default, false /* hasLocalAds */, HasRating());
|
||||
symbolName = GetSymbol(SearchMarkType::Default, HasRating());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -635,11 +621,11 @@ std::string SearchMarkPoint::GetSymbolName() const
|
|||
else if (m_isPreparing)
|
||||
symbolName = kColoredmarkSmall;
|
||||
else
|
||||
symbolName = GetSymbol(m_type, m_hasLocalAds, HasRating());
|
||||
symbolName = GetSymbol(m_type, HasRating());
|
||||
}
|
||||
else
|
||||
{
|
||||
symbolName = GetSymbol(m_type, m_hasLocalAds, HasRating());
|
||||
symbolName = GetSymbol(m_type, HasRating());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -705,13 +691,8 @@ void SearchMarks::SetDrapeEngine(ref_ptr<df::DrapeEngine> engine)
|
|||
for (uint32_t t = 0; t < searchMarkTypesCount; ++t)
|
||||
{
|
||||
auto const searchMarkType = static_cast<SearchMarkType>(t);
|
||||
symbols.push_back(GetSymbol(searchMarkType, false /* hasLocalAds */, false /* isRated */));
|
||||
symbols.push_back(GetSymbol(searchMarkType, false /* hasLocalAds */, true /* isRated */));
|
||||
if (HasLocalAdsVariant(searchMarkType))
|
||||
{
|
||||
symbols.push_back(GetSymbol(searchMarkType, true /* hasLocalAds */, false /* isRated */));
|
||||
symbols.push_back(GetSymbol(searchMarkType, true /* hasLocalAds */, true /* isRated */));
|
||||
}
|
||||
symbols.push_back(GetSymbol(searchMarkType, false /* isRated */));
|
||||
symbols.push_back(GetSymbol(searchMarkType, true /* isRated */));
|
||||
}
|
||||
|
||||
symbols.push_back(kColoredmarkSmall);
|
||||
|
@ -725,7 +706,6 @@ void SearchMarks::SetDrapeEngine(ref_ptr<df::DrapeEngine> engine)
|
|||
symbols.push_back(kPriceChipSelectedDiscount);
|
||||
|
||||
symbols.push_back(kRatedDefaultSearchIcon);
|
||||
symbols.push_back(kRatedDefaultSearchIconAds);
|
||||
|
||||
symbols.push_back(kBookingNoRatingSearchIcon);
|
||||
|
||||
|
|
|
@ -50,9 +50,9 @@ public:
|
|||
std::string const & GetMatchedName() const { return m_matchedName; }
|
||||
void SetMatchedName(std::string const & name);
|
||||
|
||||
void SetFromType(uint32_t type, bool hasLocalAds);
|
||||
void SetBookingType(bool hasLocalAds);
|
||||
void SetHotelType(bool hasLocalAds);
|
||||
void SetFromType(uint32_t type);
|
||||
void SetBookingType();
|
||||
void SetHotelType();
|
||||
void SetNotFoundType();
|
||||
|
||||
void SetPreparing(bool isPreparing);
|
||||
|
@ -93,7 +93,6 @@ protected:
|
|||
std::string GetBadgeName() const;
|
||||
|
||||
SearchMarkType m_type{};
|
||||
bool m_hasLocalAds = false;
|
||||
FeatureID m_featureID;
|
||||
// Used to pass exact search result matched string into a place page.
|
||||
std::string m_matchedName;
|
||||
|
|
|
@ -16,7 +16,6 @@ struct ProductInfo
|
|||
|
||||
static auto constexpr kInvalidRating = kInvalidRatingValue;
|
||||
|
||||
bool m_isLocalAdsCustomer = false;
|
||||
float m_ugcRating = kInvalidRating;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -105,7 +105,6 @@ std::string DebugPrint(UserMark::Type type)
|
|||
case UserMark::Type::ROUTING: return "ROUTING";
|
||||
case UserMark::Type::ROAD_WARNING: return "ROAD_WARNING";
|
||||
case UserMark::Type::SPEED_CAM: return "SPEED_CAM";
|
||||
case UserMark::Type::LOCAL_ADS: return "LOCAL_ADS";
|
||||
case UserMark::Type::TRANSIT: return "TRANSIT";
|
||||
case UserMark::Type::TRACK_INFO: return "TRACK_INFO";
|
||||
case UserMark::Type::TRACK_SELECTION: return "TRACK_SELECTION";
|
||||
|
|