forked from organicmaps/organicmaps
[ios] Implemented TTS UI.
This commit is contained in:
parent
9c5202e67f
commit
a4f980fc55
16 changed files with 429 additions and 74 deletions
|
@ -72,24 +72,6 @@ static inline NSString * formattedSize(uint64_t size)
|
|||
return [sizeString uppercaseString];
|
||||
}
|
||||
|
||||
static inline NSString * bcp47ToTwineLanguage(NSString const * bcp47LangName)
|
||||
{
|
||||
if (bcp47LangName == nil || [bcp47LangName length] < 2)
|
||||
return nil;
|
||||
|
||||
if ([bcp47LangName isEqualToString:@"zh-CN"] || [bcp47LangName isEqualToString:@"zh-CHS"]
|
||||
|| [bcp47LangName isEqualToString:@"zh-SG"])
|
||||
{
|
||||
return @"zh-Hans"; // Chinese simplified
|
||||
}
|
||||
|
||||
if ([bcp47LangName hasPrefix:@"zh"])
|
||||
return @"zh-Hant"; // Chinese traditional
|
||||
|
||||
// Taking two first symbols of a language name. For example ru-RU -> ru
|
||||
return [bcp47LangName substringToIndex:2];
|
||||
}
|
||||
|
||||
// Use only for screen dimensions CGFloat comparison
|
||||
static inline BOOL equalScreenDimensions(CGFloat left, CGFloat right)
|
||||
{
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
#pragma mark - MWMNavigationDashboardManager
|
||||
|
||||
- (void)setupRoutingDashboard:(location::FollowingInfo const &)info;
|
||||
- (void)playTurnNotifications;
|
||||
- (void)routingHidden;
|
||||
- (void)routingReady;
|
||||
- (void)routingPrepare;
|
||||
|
|
|
@ -322,11 +322,6 @@ extern NSString * const kAlohalyticsTapEventKey;
|
|||
[self.menuController setStreetName:@(info.m_sourceName.c_str())];
|
||||
}
|
||||
|
||||
- (void)playTurnNotifications
|
||||
{
|
||||
[self.navigationManager playTurnNotifications];
|
||||
}
|
||||
|
||||
- (void)handleRoutingError
|
||||
{
|
||||
self.navigationManager.state = MWMNavigationDashboardStateError;
|
||||
|
|
|
@ -51,8 +51,13 @@ using namespace routing::turns;
|
|||
// _lanes = info.m_lanes;
|
||||
_nextTurnImage = image(info.m_nextTurn, true);
|
||||
}
|
||||
_turnImage = image(info.m_turn, false);
|
||||
if (info.m_turn == TurnDirection::EnterRoundAbout || info.m_turn == TurnDirection::LeaveRoundAbout)
|
||||
|
||||
TurnDirection const turn = info.m_turn;
|
||||
_turnImage = image(turn, false);
|
||||
BOOL const isRound = turn == TurnDirection::EnterRoundAbout ||
|
||||
turn == TurnDirection::StayOnRoundAbout ||
|
||||
turn == TurnDirection::LeaveRoundAbout;
|
||||
if (isRound)
|
||||
_roundExitNumber = info.m_exitNum;
|
||||
else
|
||||
_roundExitNumber = 0;
|
||||
|
|
|
@ -43,7 +43,6 @@ typedef NS_ENUM(NSUInteger, MWMNavigationDashboardState)
|
|||
- (instancetype)initWithParentView:(UIView *)view delegate:(id<MWMNavigationDashboardManagerProtocol, MWMRoutePreviewDataSource>)delegate;
|
||||
- (void)setupDashboard:(location::FollowingInfo const &)info;
|
||||
- (void)updateDashboard;
|
||||
- (void)playTurnNotifications;
|
||||
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation;
|
||||
- (void)viewWillTransitionToSize:(CGSize)size
|
||||
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator;
|
||||
|
|
|
@ -32,7 +32,6 @@ static NSString * const kNavigationDashboardIPADXibName = @"MWMNiPadNavigationDa
|
|||
@property (weak, nonatomic) UIView * ownerView;
|
||||
|
||||
@property (nonatomic) MWMNavigationDashboardEntity * entity;
|
||||
@property (nonatomic) MWMTextToSpeech * tts;
|
||||
//@property (nonatomic) MWMLanesPanel * lanesPanel;
|
||||
@property (nonatomic) MWMNextTurnPanel * nextTurnPanel;
|
||||
@property (nonatomic) MWMRouteHelperPanelsDrawer * drawer;
|
||||
|
@ -78,12 +77,26 @@ static NSString * const kNavigationDashboardIPADXibName = @"MWMNiPadNavigationDa
|
|||
_navigationDashboard = isPortrait ? _navigationDashboardPortrait : _navigationDashboardLandscape;
|
||||
_navigationDashboardPortrait.delegate = _navigationDashboardLandscape.delegate = delegate;
|
||||
}
|
||||
_tts = [[MWMTextToSpeech alloc] init];
|
||||
_helperPanels = [NSMutableArray array];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)changedTTSStatus:(NSNotification *)notification
|
||||
{
|
||||
if (self.state != MWMNavigationDashboardStateNavigation)
|
||||
return;
|
||||
NSDictionary<NSString *, NSNumber *> * userInfo = notification.userInfo;
|
||||
BOOL const enabled = userInfo[@"on"].boolValue;
|
||||
self.navigationDashboardPortrait.soundButton.selected = enabled;
|
||||
self.navigationDashboardLandscape.soundButton.selected = enabled;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
#pragma mark - Layout
|
||||
|
||||
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation
|
||||
|
@ -142,12 +155,6 @@ static NSString * const kNavigationDashboardIPADXibName = @"MWMNiPadNavigationDa
|
|||
[self updateDashboard];
|
||||
}
|
||||
|
||||
- (void)playTurnNotifications
|
||||
{
|
||||
if (self.state == MWMNavigationDashboardStateNavigation)
|
||||
[self.tts playTurnNotifications];
|
||||
}
|
||||
|
||||
- (void)handleError
|
||||
{
|
||||
[self.routePreview stateError];
|
||||
|
@ -192,10 +199,7 @@ static NSString * const kNavigationDashboardIPADXibName = @"MWMNiPadNavigationDa
|
|||
return;
|
||||
case 1:
|
||||
if (![self.helperPanels.firstObject isKindOfClass:panel.class])
|
||||
{
|
||||
[self.helperPanels addObject:panel];
|
||||
return;
|
||||
}
|
||||
return;
|
||||
case 2:
|
||||
for (MWMRouteHelperPanel * p in self.helperPanels)
|
||||
|
@ -272,6 +276,17 @@ static NSString * const kNavigationDashboardIPADXibName = @"MWMNiPadNavigationDa
|
|||
[self.delegate didCancelRouting];
|
||||
}
|
||||
|
||||
- (IBAction)soundTap:(UIButton *)sender
|
||||
{
|
||||
BOOL const isEnable = !sender.selected;
|
||||
MWMTextToSpeech * tts = [MWMTextToSpeech tts];
|
||||
if (isEnable)
|
||||
[tts enable];
|
||||
else
|
||||
[tts disable];
|
||||
sender.selected = isEnable;
|
||||
}
|
||||
|
||||
#pragma mark - MWMNavigationGo
|
||||
|
||||
- (IBAction)navigationGoPressed:(UIButton *)sender
|
||||
|
@ -316,6 +331,13 @@ static NSString * const kNavigationDashboardIPADXibName = @"MWMNiPadNavigationDa
|
|||
- (void)showStateNavigation
|
||||
{
|
||||
[self.routePreview remove];
|
||||
MWMTextToSpeech * tts = [MWMTextToSpeech tts];
|
||||
BOOL const isNeedToEnable = tts.isNeedToEnable;
|
||||
self.navigationDashboardPortrait.soundButton.selected = isNeedToEnable;
|
||||
self.navigationDashboardLandscape.soundButton.selected = isNeedToEnable;
|
||||
if (isNeedToEnable) {
|
||||
[tts enable];
|
||||
}
|
||||
[self.navigationDashboard addToView:self.ownerView];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,33 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "std/string.hpp"
|
||||
#include "std/vector.hpp"
|
||||
|
||||
@interface MWMTextToSpeech : NSObject
|
||||
|
||||
- (instancetype)init;
|
||||
+ (instancetype)tts;
|
||||
|
||||
- (vector<std::pair<string, string>>)availableLanguages;
|
||||
- (NSString *)savedLanguage;
|
||||
- (void)setNotificationsLocale:(string const &)locale;
|
||||
- (BOOL)isNeedToEnable;
|
||||
- (void)setNeedToEnable:(BOOL)need;
|
||||
- (BOOL)isEnable;
|
||||
- (void)enable;
|
||||
- (void)disable;
|
||||
- (void)playTurnNotifications;
|
||||
|
||||
- (instancetype)init __attribute__((unavailable("call tts instead")));
|
||||
- (instancetype)copy __attribute__((unavailable("call tts instead")));
|
||||
- (instancetype)copyWithZone:(NSZone *)zone __attribute__((unavailable("call tts instead")));
|
||||
+ (instancetype)alloc __attribute__((unavailable("call tts instead")));
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable("call tts instead")));
|
||||
+ (instancetype)new __attribute__((unavailable("call tts instead")));
|
||||
|
||||
@end
|
||||
|
||||
namespace tts
|
||||
{
|
||||
|
||||
string bcp47ToTwineLanguage(NSString const * bcp47LangName);
|
||||
string translatedTwine(string const & twine);
|
||||
|
||||
} // namespace tts
|
||||
|
|
|
@ -1,21 +1,38 @@
|
|||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "Common.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "MWMTextToSpeech.h"
|
||||
|
||||
#include "Framework.h"
|
||||
#include "sound/tts/languages.hpp"
|
||||
|
||||
extern NSString * const kMwmTextToSpeechEnable = @"MWMTEXTTOSPEECH_ENABLE";
|
||||
extern NSString * const kMwmTextToSpeechDisable = @"MWMTEXTTOSPEECH_DISABLE";
|
||||
extern NSString * const kUserDefaultsTTSLanguage = @"UserDefaultsTTSLanguage";
|
||||
extern NSString * const kUserDafaultsNeedToEnableTTS = @"UserDefaultsNeedToEnableTTS";
|
||||
|
||||
@interface MWMTextToSpeech()
|
||||
{
|
||||
vector<pair<string, string>> _availableLanguages;
|
||||
}
|
||||
|
||||
@property (nonatomic) AVSpeechSynthesizer * speechSynthesizer;
|
||||
@property (nonatomic) AVSpeechSynthesisVoice * speechVoice;
|
||||
@property (nonatomic) float speechRate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMTextToSpeech
|
||||
|
||||
- (instancetype)init
|
||||
+ (instancetype)tts
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
static MWMTextToSpeech * tts = nil;
|
||||
dispatch_once(&onceToken, ^
|
||||
{
|
||||
tts = [[super alloc] initTTS];
|
||||
});
|
||||
return tts;
|
||||
}
|
||||
|
||||
- (instancetype)initTTS
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
|
@ -27,27 +44,40 @@ extern NSString * const kMwmTextToSpeechDisable = @"MWMTEXTTOSPEECH_DISABLE";
|
|||
LOG(LWARNING, ("[ setCategory]] error.", [err localizedDescription]));
|
||||
if (![audioSession setActive:YES error:&err])
|
||||
LOG(LWARNING, ("[[AVAudioSession sharedInstance] setActive]] error.", [err localizedDescription]));
|
||||
|
||||
|
||||
_availableLanguages = availableLanguages();
|
||||
|
||||
NSString * saved = self.savedLanguage;
|
||||
|
||||
string preferedLanguage ;
|
||||
if (saved.length)
|
||||
preferedLanguage = saved.UTF8String;
|
||||
else
|
||||
preferedLanguage = tts::bcp47ToTwineLanguage([AVSpeechSynthesisVoice currentLanguageCode]);
|
||||
|
||||
pair<string, string> const lan {preferedLanguage, tts::translatedTwine(preferedLanguage)};
|
||||
if (find(_availableLanguages.begin(), _availableLanguages.end(), lan) != _availableLanguages.end())
|
||||
[self setNotificationsLocale:preferedLanguage];
|
||||
else
|
||||
[self setNotificationsLocale:"en"];
|
||||
// Before 9.0 version iOS has an issue with speechRate. AVSpeechUtteranceDefaultSpeechRate does not work correctly.
|
||||
// It's a work around for iOS 7.x and 8.x.
|
||||
self.speechRate = isIOSVersionLessThan(@"7.1.1") ? 0.3 : (isIOSVersionLessThan(@"9.0.0") ? 0.15 : AVSpeechUtteranceDefaultSpeechRate);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(enable)
|
||||
name:kMwmTextToSpeechEnable
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(disable)
|
||||
name:kMwmTextToSpeechDisable
|
||||
object:nil];
|
||||
_speechRate = isIOSVersionLessThan(@"7.1.1") ? 0.3 : (isIOSVersionLessThan(@"9.0.0") ? 0.15 : AVSpeechUtteranceDefaultSpeechRate);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)dealloc
|
||||
- (vector<pair<string, string>>)availableLanguages
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
return _availableLanguages;
|
||||
}
|
||||
|
||||
- (void)setNotificationsLocale:(string const &)locale
|
||||
{
|
||||
NSUserDefaults * ud = [NSUserDefaults standardUserDefaults];
|
||||
[ud setObject:@(locale.c_str()) forKey:kUserDefaultsTTSLanguage];
|
||||
GetFramework().SetTurnNotificationsLocale(locale);
|
||||
[ud synchronize];
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
|
@ -55,16 +85,32 @@ extern NSString * const kMwmTextToSpeechDisable = @"MWMTEXTTOSPEECH_DISABLE";
|
|||
return _speechSynthesizer != nil && _speechVoice != nil;
|
||||
}
|
||||
|
||||
- (BOOL)isNeedToEnable
|
||||
{
|
||||
return [[NSUserDefaults standardUserDefaults] boolForKey:kUserDafaultsNeedToEnableTTS];
|
||||
}
|
||||
|
||||
- (void)setNeedToEnable:(BOOL)need
|
||||
{
|
||||
NSUserDefaults * ud = [NSUserDefaults standardUserDefaults];
|
||||
[ud setBool:need forKey:kUserDafaultsNeedToEnableTTS];
|
||||
[ud synchronize];
|
||||
}
|
||||
|
||||
- (void)enable
|
||||
{
|
||||
if (![self isValid])
|
||||
[self createSynthesizer];
|
||||
|
||||
GetFramework().EnableTurnNotifications(true);
|
||||
[self setNeedToEnable:YES];
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
|
||||
{
|
||||
if (![self isValid])
|
||||
[self createSynthesizer];
|
||||
GetFramework().EnableTurnNotifications(true);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)disable
|
||||
{
|
||||
[self setNeedToEnable:NO];
|
||||
GetFramework().EnableTurnNotifications(false);
|
||||
}
|
||||
|
||||
|
@ -73,13 +119,17 @@ extern NSString * const kMwmTextToSpeechDisable = @"MWMTEXTTOSPEECH_DISABLE";
|
|||
return GetFramework().AreTurnNotificationsEnabled() ? YES : NO;
|
||||
}
|
||||
|
||||
- (NSString *)savedLanguage
|
||||
{
|
||||
return [[NSUserDefaults standardUserDefaults] stringForKey:kUserDefaultsTTSLanguage];
|
||||
}
|
||||
|
||||
- (void)createSynthesizer
|
||||
{
|
||||
self.speechSynthesizer = [[AVSpeechSynthesizer alloc] init];
|
||||
|
||||
[self createVoice:self.savedLanguage];
|
||||
// TODO(vbykoianko) Use [NSLocale preferredLanguages] instead of [AVSpeechSynthesisVoice currentLanguageCode].
|
||||
// [AVSpeechSynthesisVoice currentLanguageCode] is used now because of we need a language code in BCP-47.
|
||||
[self createVoice:[AVSpeechSynthesisVoice currentLanguageCode]];
|
||||
}
|
||||
|
||||
- (void)createVoice:(NSString *)locale
|
||||
|
@ -113,13 +163,13 @@ extern NSString * const kMwmTextToSpeechDisable = @"MWMTEXTTOSPEECH_DISABLE";
|
|||
}
|
||||
|
||||
self.speechVoice = [AVSpeechSynthesisVoice voiceWithLanguage:locale];
|
||||
NSString const * twineLang = bcp47ToTwineLanguage(locale);
|
||||
if (twineLang == nil)
|
||||
string const twineLang = tts::bcp47ToTwineLanguage(locale);
|
||||
if (twineLang.empty())
|
||||
{
|
||||
LOG(LERROR, ("Cannot convert UI locale or default locale to twine language. MWMTestToSpeech is invalid."));
|
||||
return; // self is not valid.
|
||||
}
|
||||
GetFramework().SetTurnNotificationsLocale([twineLang UTF8String]);
|
||||
GetFramework().SetTurnNotificationsLocale(twineLang);
|
||||
}
|
||||
|
||||
- (void)speakOneString:(NSString *)textToSpeak
|
||||
|
@ -150,4 +200,59 @@ extern NSString * const kMwmTextToSpeechDisable = @"MWMTEXTTOSPEECH_DISABLE";
|
|||
[self speakOneString:@(text.c_str())];
|
||||
}
|
||||
|
||||
static vector<pair<string, string>> availableLanguages()
|
||||
{
|
||||
NSArray<AVSpeechSynthesisVoice *> * voices = [AVSpeechSynthesisVoice speechVoices];
|
||||
vector<string> native(voices.count);
|
||||
for (AVSpeechSynthesisVoice * v in voices)
|
||||
native.push_back(tts::bcp47ToTwineLanguage(v.language));
|
||||
|
||||
sort(native.begin(), native.end());
|
||||
using namespace routing::turns::sound;
|
||||
vector<pair<string, string>> result;
|
||||
for (auto const & p : kLanguageList)
|
||||
{
|
||||
if (find(native.begin(), native.end(), p.first) != native.end())
|
||||
result.push_back(p);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace tts
|
||||
{
|
||||
|
||||
string bcp47ToTwineLanguage(NSString const * bcp47LangName)
|
||||
{
|
||||
if (bcp47LangName == nil || [bcp47LangName length] < 2)
|
||||
return nil;
|
||||
|
||||
if ([bcp47LangName isEqualToString:@"zh-CN"] || [bcp47LangName isEqualToString:@"zh-CHS"]
|
||||
|| [bcp47LangName isEqualToString:@"zh-SG"])
|
||||
{
|
||||
return "zh-Hans"; // Chinese simplified
|
||||
}
|
||||
|
||||
if ([bcp47LangName hasPrefix:@"zh"])
|
||||
return "zh-Hant"; // Chinese traditional
|
||||
|
||||
// Taking two first symbols of a language name. For example ru-RU -> ru
|
||||
return [[bcp47LangName substringToIndex:2] UTF8String];
|
||||
}
|
||||
|
||||
string translatedTwine(string const & twine)
|
||||
{
|
||||
auto const & list = routing::turns::sound::kLanguageList;
|
||||
auto const it = find_if(list.begin(), list.end(), [&twine](pair<string, string> const & pair)
|
||||
{
|
||||
return pair.first == twine;
|
||||
});
|
||||
|
||||
if (it != list.end())
|
||||
return it->second;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace tts
|
|
@ -12,6 +12,7 @@
|
|||
@property (weak, nonatomic) IBOutlet UILabel * arrivalsTimeLabel;
|
||||
@property (weak, nonatomic) IBOutlet UILabel * roundRoadLabel;
|
||||
@property (weak, nonatomic) IBOutlet UILabel * streetLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIButton * soundButton;
|
||||
@property (weak, nonatomic) IBOutlet UISlider * progress;
|
||||
|
||||
- (void)configureWithEntity:(MWMNavigationDashboardEntity *)entity;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#import "MWMAPIBar.h"
|
||||
#import "MWMMapViewControlsManager.h"
|
||||
#import "RouteState.h"
|
||||
#import "MWMTextToSpeech.h"
|
||||
#import "UIFont+MapsMeFonts.h"
|
||||
#import "UIViewController+Navigation.h"
|
||||
|
||||
|
@ -143,7 +144,7 @@ typedef NS_ENUM(NSUInteger, UserTouchesAction)
|
|||
if (res.IsValid())
|
||||
[self.controlsManager setupRoutingDashboard:res];
|
||||
|
||||
[self.controlsManager playTurnNotifications];
|
||||
[[MWMTextToSpeech tts] playTurnNotifications];
|
||||
}
|
||||
|
||||
- (void)onCompassUpdate:(location::CompassInfo const &)info
|
||||
|
|
|
@ -47,6 +47,8 @@ static NSString * const kBundleVersion = @"BundleVersion";
|
|||
extern string const kCountryCodeKey;
|
||||
extern string const kUniqueIdKey;
|
||||
extern string const kLanguageKey;
|
||||
extern NSString * const kUserDefaultsTTSLanguage;
|
||||
extern NSString * const kUserDafaultsNeedToEnableTTS;
|
||||
|
||||
/// Adds needed localized strings to C++ code
|
||||
/// @TODO Refactor localization mechanism to make it simpler
|
||||
|
@ -202,11 +204,13 @@ void InitLocalizedStrings()
|
|||
[self incrementSessionCount];
|
||||
[self showAlertIfRequired];
|
||||
}
|
||||
|
||||
|
||||
Framework & f = GetFramework();
|
||||
application.applicationIconBadgeNumber = f.GetCountryTree().GetActiveMapLayout().GetOutOfDateCount();
|
||||
f.GetLocationState()->InvalidatePosition();
|
||||
|
||||
[self enableTTSForTheFirstTime];
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
@ -529,6 +533,17 @@ void InitLocalizedStrings()
|
|||
[RouteState remove];
|
||||
}
|
||||
|
||||
#pragma mark - TTS
|
||||
|
||||
- (void)enableTTSForTheFirstTime
|
||||
{
|
||||
NSUserDefaults * ud = [NSUserDefaults standardUserDefaults];
|
||||
if ([ud stringForKey:kUserDefaultsTTSLanguage].length)
|
||||
return;
|
||||
[ud setBool:YES forKey:kUserDafaultsNeedToEnableTTS];
|
||||
[ud synchronize];
|
||||
}
|
||||
|
||||
#pragma mark - Standby
|
||||
|
||||
- (void)enableStandby
|
||||
|
|
5
iphone/Maps/MWMTTSLanguageViewController.h
Normal file
5
iphone/Maps/MWMTTSLanguageViewController.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#import "TableViewController.h"
|
||||
|
||||
@interface MWMTTSLanguageViewController : TableViewController
|
||||
|
||||
@end
|
39
iphone/Maps/MWMTTSLanguageViewController.mm
Normal file
39
iphone/Maps/MWMTTSLanguageViewController.mm
Normal file
|
@ -0,0 +1,39 @@
|
|||
#import "MWMTextToSpeech.h"
|
||||
#import "MWMTTSLanguageViewController.h"
|
||||
#import "MWMTTSSettingsViewController.h"
|
||||
#import "SelectableCell.h"
|
||||
|
||||
static NSString * const kUnwingSegueIdentifier = @"UnwindToTTSSettings";
|
||||
|
||||
@implementation MWMTTSLanguageViewController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.title = L(@"pref_tts_other_section_title");
|
||||
}
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(SelectableCell *)sender
|
||||
{
|
||||
if (![segue.identifier isEqualToString:kUnwingSegueIdentifier])
|
||||
return;
|
||||
MWMTTSSettingsViewController * dest = segue.destinationViewController;
|
||||
NSUInteger const row = [self.tableView indexPathForCell:sender].row;
|
||||
[dest setAdditionalTTSLanguage:[[MWMTextToSpeech tts] availableLanguages][row]];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource && UITableViewDelegate
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return [[MWMTextToSpeech tts] availableLanguages].size();
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
SelectableCell * cell = (SelectableCell *)[tableView dequeueReusableCellWithIdentifier:[SelectableCell className]];
|
||||
cell.titleLabel.text = @([[MWMTextToSpeech tts] availableLanguages][indexPath.row].second.c_str());
|
||||
return cell;
|
||||
}
|
||||
|
||||
@end
|
7
iphone/Maps/MWMTTSSettingsViewController.h
Normal file
7
iphone/Maps/MWMTTSSettingsViewController.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#import "TableViewController.h"
|
||||
|
||||
@interface MWMTTSSettingsViewController : TableViewController
|
||||
|
||||
- (void)setAdditionalTTSLanguage:(std::pair<string, string> const &)l;
|
||||
|
||||
@end
|
134
iphone/Maps/MWMTTSSettingsViewController.mm
Normal file
134
iphone/Maps/MWMTTSSettingsViewController.mm
Normal file
|
@ -0,0 +1,134 @@
|
|||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "LinkCell.h"
|
||||
#import "MWMTextToSpeech.h"
|
||||
#import "MWMTTSSettingsViewController.h"
|
||||
#import "SelectableCell.h"
|
||||
|
||||
static NSString * kSelectTTSLanguageSegueName = @"TTSLanguage";
|
||||
|
||||
using namespace std;
|
||||
|
||||
@interface MWMTTSSettingsViewController ()
|
||||
{
|
||||
pair<string, string> _additionalTTSLanguage;
|
||||
vector<pair<string, string>> _languages;
|
||||
}
|
||||
|
||||
@property (nonatomic) BOOL isLocaleLanguageAbsent;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MWMTTSSettingsViewController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.title = L(@"pref_tts_language_title");
|
||||
MWMTextToSpeech * tts = [MWMTextToSpeech tts];
|
||||
|
||||
_languages.reserve(3);
|
||||
auto const & v = tts.availableLanguages;
|
||||
pair<string, string> const standart = v.front();
|
||||
_languages.push_back(standart);
|
||||
|
||||
using namespace tts;
|
||||
string const current = bcp47ToTwineLanguage([AVSpeechSynthesisVoice currentLanguageCode]);
|
||||
if (current != standart.first && !current.empty())
|
||||
{
|
||||
string const translated = translatedTwine(current);
|
||||
pair<string, string> const cur {current, translated};
|
||||
if (translated.empty() || find(v.begin(), v.end(), cur) != v.end())
|
||||
_languages.push_back(cur);
|
||||
else
|
||||
self.isLocaleLanguageAbsent = YES;
|
||||
}
|
||||
string const savedLanguage = tts.savedLanguage.UTF8String;
|
||||
if (savedLanguage != current && savedLanguage != standart.first && !savedLanguage.empty())
|
||||
_languages.push_back({savedLanguage, translatedTwine(savedLanguage)});
|
||||
}
|
||||
|
||||
- (IBAction)unwind:(id)sender
|
||||
{
|
||||
size_t const size = _languages.size();
|
||||
switch (size)
|
||||
{
|
||||
case 1:
|
||||
_languages.push_back(_additionalTTSLanguage);
|
||||
break;
|
||||
case 2:
|
||||
if (self.isLocaleLanguageAbsent)
|
||||
_languages[size - 1] = _additionalTTSLanguage;
|
||||
else
|
||||
_languages.push_back(_additionalTTSLanguage);
|
||||
break;
|
||||
case 3:
|
||||
_languages[size - 1] = _additionalTTSLanguage;
|
||||
break;
|
||||
default:
|
||||
NSAssert(false, @"Incorrect language's count");
|
||||
break;
|
||||
}
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)setAdditionalTTSLanguage:(pair<string, string> const &)l
|
||||
{
|
||||
[[MWMTextToSpeech tts] setNotificationsLocale:l.first];
|
||||
_additionalTTSLanguage = l;
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
if (section == 0)
|
||||
return _languages.size() + 1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.section == 0 && indexPath.row != _languages.size())
|
||||
{
|
||||
SelectableCell * cell = (SelectableCell *)[tableView dequeueReusableCellWithIdentifier:[SelectableCell className]];
|
||||
pair<string, string> const p = _languages[indexPath.row];
|
||||
cell.titleLabel.text = @(p.second.c_str());
|
||||
BOOL const isSelected = [@(p.first.c_str()) isEqualToString:[[MWMTextToSpeech tts] savedLanguage]];
|
||||
cell.accessoryType = isSelected ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
|
||||
return cell;
|
||||
}
|
||||
else
|
||||
{
|
||||
LinkCell * cell = (LinkCell *)[tableView dequeueReusableCellWithIdentifier:[LinkCell className]];
|
||||
cell.titleLabel.text = indexPath.section == 0 ? L(@"pref_tts_other_section_title") : L(@"pref_tts_how_to_set_up_voice");
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (indexPath.section == 0)
|
||||
{
|
||||
if (indexPath.row == _languages.size())
|
||||
{
|
||||
[self performSegueWithIdentifier:kSelectTTSLanguageSegueName sender:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
[[MWMTextToSpeech tts] setNotificationsLocale:_languages[indexPath.row].first];
|
||||
[tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#warning need to add help
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
#import "SettingsViewController.h"
|
||||
#import "SwitchCell.h"
|
||||
#import "SelectableCell.h"
|
||||
|
@ -8,6 +7,7 @@
|
|||
#import "MapsAppDelegate.h"
|
||||
#import "Statistics.h"
|
||||
#import "MWMMapViewControlsManager.h"
|
||||
#import "MWMTextToSpeech.h"
|
||||
|
||||
#include "Framework.h"
|
||||
|
||||
|
@ -16,14 +16,16 @@
|
|||
#include "platform/preferred_languages.hpp"
|
||||
|
||||
extern char const * kStatisticsEnabledSettingsKey;
|
||||
extern NSString * const kTTSStatusWasChangedNotification = @"TTFStatusWasChangedFromSettingsNotification";
|
||||
|
||||
typedef NS_ENUM(NSUInteger, Section)
|
||||
{
|
||||
SectionMetrics,
|
||||
SectionZoomButtons,
|
||||
SectionRouting,
|
||||
SectionCalibration,
|
||||
SectionStatistics,
|
||||
SectionCount
|
||||
SectionCount // Must be latest value!
|
||||
};
|
||||
|
||||
@interface SettingsViewController () <SwitchCellDelegate>
|
||||
|
@ -60,7 +62,7 @@ typedef NS_ENUM(NSUInteger, Section)
|
|||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
if (section == SectionMetrics)
|
||||
if (section == SectionMetrics || section == SectionRouting)
|
||||
return 2;
|
||||
else
|
||||
return 1;
|
||||
|
@ -110,7 +112,23 @@ typedef NS_ENUM(NSUInteger, Section)
|
|||
customCell.titleLabel.text = L(@"pref_calibration_title");
|
||||
customCell.delegate = self;
|
||||
}
|
||||
|
||||
else if (indexPath.section == SectionRouting)
|
||||
{
|
||||
if (indexPath.row == 0)
|
||||
{
|
||||
cell = [tableView dequeueReusableCellWithIdentifier:[SwitchCell className]];
|
||||
SwitchCell * customCell = (SwitchCell *)cell;
|
||||
customCell.switchButton.on = [[MWMTextToSpeech tts] isNeedToEnable];
|
||||
customCell.titleLabel.text = L(@"pref_tts_enable_title");
|
||||
customCell.delegate = self;
|
||||
}
|
||||
else
|
||||
{
|
||||
cell = [tableView dequeueReusableCellWithIdentifier:[LinkCell className]];
|
||||
LinkCell * customCell = (LinkCell *)cell;
|
||||
customCell.titleLabel.text = L(@"pref_tts_language_title");
|
||||
}
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
|
@ -144,6 +162,13 @@ typedef NS_ENUM(NSUInteger, Section)
|
|||
{
|
||||
Settings::Set("CompassCalibrationEnabled", (bool)value);
|
||||
}
|
||||
else if (indexPath.section == SectionRouting)
|
||||
{
|
||||
[[MWMTextToSpeech tts] setNeedToEnable:value];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kTTSStatusWasChangedNotification
|
||||
object:nil
|
||||
userInfo:@{@"on" : @(value)}];
|
||||
}
|
||||
}
|
||||
|
||||
Settings::Units unitsForIndex(NSInteger index)
|
||||
|
@ -166,6 +191,8 @@ Settings::Units unitsForIndex(NSInteger index)
|
|||
{
|
||||
if (section == SectionMetrics)
|
||||
return L(@"measurement_units");
|
||||
else if (section == SectionRouting)
|
||||
return L(@"prefs_group_route");
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue