diff --git a/iphone/Maps/Classes/BottomMenu.h b/iphone/Maps/Classes/BottomMenu.h index 30875d51b5..ffeda695b7 100644 --- a/iphone/Maps/Classes/BottomMenu.h +++ b/iphone/Maps/Classes/BottomMenu.h @@ -4,8 +4,7 @@ @class BottomMenu; @protocol BottomMenuDelegate -- (void)bottomMenu:(BottomMenu *)menu didPressItemWithName:(NSString *)itemName; -- (void)bottomMenuDidPressBuyButton:(BottomMenu *)menu; +- (void)bottomMenu:(BottomMenu *)menu didPressItemWithName:(NSString *)itemName appURL:(NSString *)appURL webURL:(NSString *)webURL; @end @@ -13,8 +12,6 @@ @property (weak) id delegate; -- (instancetype)initWithFrame:(CGRect)frame items:(NSArray *)items; - - (void)setMenuHidden:(BOOL)hidden animated:(BOOL)animated; @property (readonly, nonatomic) BOOL menuHidden; diff --git a/iphone/Maps/Classes/BottomMenu.mm b/iphone/Maps/Classes/BottomMenu.mm index fd4f8e4b7a..209a2488ea 100644 --- a/iphone/Maps/Classes/BottomMenu.mm +++ b/iphone/Maps/Classes/BottomMenu.mm @@ -4,83 +4,182 @@ #import "UIKitCategories.h" #include "../../../platform/platform.hpp" #import "AppInfo.h" +#import "ImageDownloader.h" -@interface BottomMenu () +@interface BottomMenu () @property (nonatomic) UITableView * tableView; @property (nonatomic) SolidTouchView * fadeView; - @property (nonatomic) NSArray * items; +@property (nonatomic) NSMutableDictionary * imageDownloaders; @end @implementation BottomMenu -- (instancetype)initWithFrame:(CGRect)frame items:(NSArray *)items +- (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; - self.items = items; - self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self addSubview:self.fadeView]; - CGFloat menuHeight = [items count] * [BottomMenuCell cellHeight]; - if (!GetPlatform().IsPro()) - menuHeight += [BottomMenuCell cellHeight]; - - self.tableView.frame = CGRectMake(0, 0, self.width, menuHeight); [self addSubview:self.tableView]; + _menuHidden = YES; + + self.imageDownloaders = [[NSMutableDictionary alloc] init]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appInfoSynced:) name:AppInfoSyncedNotification object:nil]; + return self; } +- (NSArray *)generateItems +{ + NSMutableArray * items = [[NSMutableArray alloc] init]; + + if (!GetPlatform().IsPro()) + [items addObject:@{@"Id" : @"MWMPro", @"Title" : NSLocalizedString(@"become_a_pro", nil), @"Icon" : [UIImage imageNamed:@"MWMProIcon"], @"Color" : @"15c783"}]; + + NSArray * firstGroup = @[@{@"Id" : @"Maps", @"Title" : NSLocalizedString(@"download_maps", nil), @"Icon" : [UIImage imageNamed:@"IconMap"]}, + @{@"Id" : @"Settings", @"Title" : NSLocalizedString(@"settings", nil), @"Icon" : [UIImage imageNamed:@"IconSettings"]}, + @{@"Id" : @"Share", @"Title" : NSLocalizedString(@"share_my_location", nil), @"Icon" : [UIImage imageNamed:@"IconShare"]}]; + + [items addObjectsFromArray:firstGroup]; + + NSArray * serverItems = [[AppInfo sharedInfo] featureValue:AppFeatureBottomMenuItems forKey:@"Items"]; + if ([serverItems count]) + [items addObjectsFromArray:serverItems]; + + [items addObject:@{@"Id" : @"MoreApps", @"Title" : NSLocalizedString(@"more_apps_title", nil), @"Icon" : [UIImage imageNamed:@"IconMoreApps"]}]; + + return items; +} + +- (void)appInfoSynced:(NSNotification *)notification +{ + [self reload]; +} + - (void)didMoveToSuperview { - [self setMenuHidden:YES animated:NO]; + [self reload]; +} + +- (void)reload +{ + self.items = [self generateItems]; + [self.tableView reloadData]; + [self align]; +} + +- (void)align +{ + CGFloat menuHeight = [self.items count] * [BottomMenuCell cellHeight]; + if (self.superview.width > self.superview.height) + menuHeight = MIN(menuHeight, 228); + + self.tableView.frame = CGRectMake(self.tableView.minX, self.tableView.minY, self.width, menuHeight); + + [self setMenuHidden:self.menuHidden animated:NO]; +} + +- (void)layoutSubviews +{ + [self align]; } #pragma mark - TableView - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - if (GetPlatform().IsPro()) - return [self.items count]; - else - return (section == 0) ? 1 : [self.items count]; -} - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return GetPlatform().IsPro() ? 1 : 2; + return [self.items count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - if (GetPlatform().IsPro() || (!GetPlatform().IsPro() && indexPath.section == 1)) + NSDictionary * item = self.items[indexPath.row]; + + BottomMenuCell * cell = (BottomMenuCell *)[tableView dequeueReusableCellWithIdentifier:[BottomMenuCell className]]; + if (!cell) + cell = [[BottomMenuCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[BottomMenuCell className]]; + + if (item[@"Icon"]) { - BottomMenuCell * cell = (BottomMenuCell *)[tableView dequeueReusableCellWithIdentifier:[BottomMenuCell className]]; - if (!cell) - cell = [[BottomMenuCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[BottomMenuCell className]]; - - cell.iconImageView.image = self.items[indexPath.row][@"Icon"]; - cell.titleLabel.text = self.items[indexPath.row][@"Title"]; - cell.titleLabel.textColor = [UIColor whiteColor]; - - return cell; + cell.iconImageView.image = item[@"Icon"]; } - else + else if (item[@"IconURLs"]) { - BottomMenuCell * cell = (BottomMenuCell *)[tableView dequeueReusableCellWithIdentifier:[BottomMenuCell className]]; - if (!cell) - cell = [[BottomMenuCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[BottomMenuCell className]]; + NSString * itemId = item[@"Id"]; + NSString * imageName = itemId; + NSString * imagePath = [[self imagesPath] stringByAppendingPathComponent:imageName]; + UIImage * image = [UIImage imageNamed:imagePath]; + if (image) + { + cell.iconImageView.image = image; + } + else if (!self.imageDownloaders[itemId]) + { + ImageDownloader * downloader = [[ImageDownloader alloc] init]; + downloader.delegate = self; + downloader.objectId = itemId; + self.imageDownloaders[itemId] = downloader; - cell.titleLabel.text = NSLocalizedString(@"become_a_pro", nil); - cell.iconImageView.image = [UIImage imageNamed:@"MWMProIcon"]; - cell.titleLabel.textColor = [UIColor applicationColor]; - - return cell; + NSDictionary * links = item[@"IconURLs"]; + NSString * key = [UIScreen mainScreen].scale == 2 ? @"2x" : @"1x"; + NSString * link = links[key]; + [downloader startDownloadingWithURL:[NSURL URLWithString:link]]; + } } + + cell.titleLabel.textColor = item[@"Color"] ? [UIColor colorWithColorCode:item[@"Color"]] : [UIColor whiteColor]; + + if (item[@"Title"]) + { + cell.titleLabel.text = item[@"Title"]; + } + else if (item[@"Titles"]) + { + NSDictionary * titles = item[@"Titles"]; + NSString * title = titles[[[NSLocale preferredLanguages] firstObject]]; + if (!title) + title = titles[@"*"]; + + cell.titleLabel.text = title; + } + + return cell; +} + +- (void)imageDownloaderDidFinishLoading:(ImageDownloader *)downloader +{ + NSInteger row = 0; + for (NSInteger i = 0; i < [self.items count]; i++) + { + if ([self.items[i][@"Id"] isEqualToString:downloader.objectId]) + { + row = i; + break; + } + } + BottomMenuCell * cell = (BottomMenuCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0]]; + cell.iconImageView.image = downloader.image; + + NSString * imageName = downloader.objectId; + NSString * imagePath = [[self imagesPath] stringByAppendingPathComponent:imageName]; + [UIImagePNGRepresentation(downloader.image) writeToFile:imagePath atomically:YES]; + + [self.imageDownloaders removeObjectForKey:downloader.objectId]; +} + +- (NSString *)imagesPath +{ + NSString * libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject]; + NSString * path = [libraryPath stringByAppendingPathComponent:@"bottom_menu_images/"]; + if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:nil]) + [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:NO attributes:NO error:nil]; + return path; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath @@ -90,11 +189,12 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - if (GetPlatform().IsPro() || (!GetPlatform().IsPro() && indexPath.section == 1)) - [self.delegate bottomMenu:self didPressItemWithName:self.items[indexPath.row][@"Item"]]; - else - [self.delegate bottomMenuDidPressBuyButton:self]; - + NSDictionary * item = self.items[indexPath.row]; + NSDictionary * urls = item[@"WebURLs"]; + NSString * url = urls[[[NSLocale preferredLanguages] firstObject]]; + if (!url) + url = urls[@"*"]; + [self.delegate bottomMenu:self didPressItemWithName:item[@"Id"] appURL:item[@"AppURL"] webURL:url]; [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES]; } @@ -135,7 +235,7 @@ _tableView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth; _tableView.delegate = self; _tableView.dataSource = self; - _tableView.scrollEnabled = NO; + _tableView.alwaysBounceVertical = NO; _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; _tableView.backgroundColor = [UIColor colorWithColorCode:@"444651"]; } @@ -155,4 +255,9 @@ return _fadeView; } +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + @end diff --git a/iphone/Maps/Classes/ImageDownloader.h b/iphone/Maps/Classes/ImageDownloader.h new file mode 100644 index 0000000000..5dd5f6b00b --- /dev/null +++ b/iphone/Maps/Classes/ImageDownloader.h @@ -0,0 +1,19 @@ + +#import + +@class ImageDownloader; +@protocol ImageDownloaderDelegate + +- (void)imageDownloaderDidFinishLoading:(ImageDownloader *)downloader; + +@end + +@interface ImageDownloader : NSObject + +@property (nonatomic, weak) id delegate; +@property (nonatomic) UIImage * image; +@property (nonatomic) NSString * objectId; + +- (void)startDownloadingWithURL:(NSURL *)URL; + +@end diff --git a/iphone/Maps/Classes/ImageDownloader.m b/iphone/Maps/Classes/ImageDownloader.m new file mode 100644 index 0000000000..da9d4c4d58 --- /dev/null +++ b/iphone/Maps/Classes/ImageDownloader.m @@ -0,0 +1,18 @@ + +#import "ImageDownloader.h" + +@implementation ImageDownloader + +- (void)startDownloadingWithURL:(NSURL *)URL +{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + NSURLRequest * request = [NSURLRequest requestWithURL:URL cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:20]; + NSData * data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; + self.image = [UIImage imageWithData:data scale:[UIScreen mainScreen].scale]; + dispatch_sync(dispatch_get_main_queue(), ^{ + [self.delegate imageDownloaderDidFinishLoading:self]; + }); + }); +} + +@end diff --git a/iphone/Maps/Classes/MapViewController.mm b/iphone/Maps/Classes/MapViewController.mm index 90ba4b42e5..930444ddbf 100644 --- a/iphone/Maps/Classes/MapViewController.mm +++ b/iphone/Maps/Classes/MapViewController.mm @@ -708,11 +708,7 @@ const long long LITE_IDL = 431183278L; { if (!_bottomMenu) { - NSArray * items = @[@{@"Item" : @"Maps", @"Title" : NSLocalizedString(@"download_maps", nil), @"Icon" : [UIImage imageNamed:@"IconMap"]}, - @{@"Item" : @"Settings", @"Title" : NSLocalizedString(@"settings", nil), @"Icon" : [UIImage imageNamed:@"IconSettings"]}, - @{@"Item" : @"Share", @"Title" : NSLocalizedString(@"share_my_location", nil), @"Icon" : [UIImage imageNamed:@"IconShare"]}, - @{@"Item" : @"MoreApps", @"Title" : NSLocalizedString(@"more_apps_title", nil), @"Icon" : [UIImage imageNamed:@"IconMoreApps"]}]; - _bottomMenu = [[BottomMenu alloc] initWithFrame:self.view.bounds items:items]; + _bottomMenu = [[BottomMenu alloc] initWithFrame:self.view.bounds]; _bottomMenu.delegate = self; } return _bottomMenu; @@ -927,13 +923,7 @@ const long long LITE_IDL = 431183278L; #pragma mark - BottomMenuDelegate -- (void)bottomMenuDidPressBuyButton:(BottomMenu *)menu -{ - [[Statistics instance] logProposalReason:@"Pro button in menu" withAnswer:@"YES"]; - [[UIApplication sharedApplication] openProVersionFrom:@"ios_bottom_menu"]; -} - -- (void)bottomMenu:(BottomMenu *)menu didPressItemWithName:(NSString *)itemName +- (void)bottomMenu:(BottomMenu *)menu didPressItemWithName:(NSString *)itemName appURL:(NSString *)appURL webURL:(NSString *)webURL { if ([itemName isEqualToString:@"Maps"]) { @@ -941,7 +931,7 @@ const long long LITE_IDL = 431183278L; } else if ([itemName isEqualToString:@"Settings"]) { - SettingsViewController * vc = [self.mainStoryboard instantiateViewControllerWithIdentifier:[SettingsViewController className]]; + SettingsAndMoreVC * vc = [[SettingsAndMoreVC alloc] init]; [self.navigationController pushViewController:vc animated:YES]; } else if ([itemName isEqualToString:@"Share"]) @@ -967,6 +957,17 @@ const long long LITE_IDL = 431183278L; MoreAppsVC * vc = [[MoreAppsVC alloc] init]; [self.navigationController pushViewController:vc animated:YES]; } + else if ([itemName isEqualToString:@"MWMPro"]) + { + [[Statistics instance] logProposalReason:@"Pro button in menu" withAnswer:@"YES"]; + [[UIApplication sharedApplication] openProVersionFrom:@"ios_bottom_menu"]; + } + else + { + [menu setMenuHidden:YES animated:YES]; + [[Statistics instance] logEvent:@"Bottom menu item clicked" withParameters:@{@"Item" : itemName, @"Country": [AppInfo sharedInfo].countryCode}]; + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:(appURL ? appURL : webURL)]]; + } } - (void)buyButtonPressed:(id)sender diff --git a/iphone/Maps/Statistics/AppInfo.h b/iphone/Maps/Statistics/AppInfo.h index 3eab7779a0..7241fd228b 100644 --- a/iphone/Maps/Statistics/AppInfo.h +++ b/iphone/Maps/Statistics/AppInfo.h @@ -6,6 +6,9 @@ extern NSString * const AppFeatureInterstitial; extern NSString * const AppFeatureBanner; extern NSString * const AppFeatureProButtonOnMap; extern NSString * const AppFeatureMoreAppsBanner; +extern NSString * const AppFeatureBottomMenuItems; + +extern NSString * const AppInfoSyncedNotification; @interface AppInfo : NSObject diff --git a/iphone/Maps/Statistics/AppInfo.mm b/iphone/Maps/Statistics/AppInfo.mm index 5f96829027..de7f11fe45 100644 --- a/iphone/Maps/Statistics/AppInfo.mm +++ b/iphone/Maps/Statistics/AppInfo.mm @@ -10,6 +10,9 @@ NSString * const AppFeatureInterstitial = @"AppFeatureInterstitial"; NSString * const AppFeatureBanner = @"AppFeatureBanner"; NSString * const AppFeatureProButtonOnMap = @"AppFeatureProButtonOnMap"; NSString * const AppFeatureMoreAppsBanner = @"AppFeatureMoreAppsBanner"; +NSString * const AppFeatureBottomMenuItems = @"AppFeatureBottomMenuItems"; + +NSString * const AppInfoSyncedNotification = @"AppInfoSyncedNotification"; @interface AppInfo () @@ -39,13 +42,16 @@ NSString * const AppFeatureMoreAppsBanner = @"AppFeatureMoreAppsBanner"; - (void)update { - NSString * urlString = @"http://application.server/ios/features.json"; +#warning <#message#> + NSString * urlString = @"http://application.server/ios/features_test.json"; NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:20]; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * r, NSData * d, NSError * e){ if ([(NSHTTPURLResponse *)r statusCode] == 200) { [d writeToFile:[self featuresPath] atomically:YES]; + _features = nil; [self.reachability stopNotifier]; + [[NSNotificationCenter defaultCenter] postNotificationName:AppInfoSyncedNotification object:nil]; } else {