diff --git a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Sound/MWMTextToSpeech.h b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Sound/MWMTextToSpeech.h index f05440530c..f8000a2f61 100644 --- a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Sound/MWMTextToSpeech.h +++ b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Sound/MWMTextToSpeech.h @@ -4,7 +4,6 @@ @interface MWMTextToSpeech : NSObject + (instancetype)tts; -+ (void)activateAudioSession; // Returns a list of available languages in the following format: // * name in bcp47; // * localized name; diff --git a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Sound/MWMTextToSpeech.mm b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Sound/MWMTextToSpeech.mm index e37574f4d3..54a8c0b797 100644 --- a/iphone/Maps/Classes/CustomViews/NavigationDashboard/Sound/MWMTextToSpeech.mm +++ b/iphone/Maps/Classes/CustomViews/NavigationDashboard/Sound/MWMTextToSpeech.mm @@ -14,7 +14,7 @@ static NSString * const DEFAULT_LANG = @"en-US"; using namespace locale_translator; -@interface MWMTextToSpeech() +@interface MWMTextToSpeech() { vector> _availableLanguages; } @@ -22,6 +22,8 @@ using namespace locale_translator; @property (nonatomic) AVSpeechSynthesizer * speechSynthesizer; @property (nonatomic) AVSpeechSynthesisVoice * speechVoice; @property (nonatomic) float speechRate; +@property (nonatomic) AVAudioSession * audioSession; +@property (nonatomic) NSInteger notificationsToSpeak; @end @@ -64,23 +66,19 @@ using namespace locale_translator; // 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. _speechRate = isIOSVersionLessThan(@"7.1.1") ? 0.3 : (isIOSVersionLessThan(@"9.0.0") ? 0.15 : AVSpeechUtteranceDefaultSpeechRate); + + NSError * err = nil; + _audioSession = [AVAudioSession sharedInstance]; + if (![_audioSession setCategory:AVAudioSessionCategoryPlayback + withOptions:AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers + error:&err]) + { + LOG(LWARNING, ("[ setCategory]] error.", [err localizedDescription])); + } } return self; } -+ (void)activateAudioSession -{ - NSError * err = nil; - AVAudioSession * audioSession = [AVAudioSession sharedInstance]; - if (![audioSession setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&err]) - { - LOG(LWARNING, ("[ setCategory]] error.", [err localizedDescription])); - return; - } - if (![audioSession setActive:YES error:&err]) - LOG(LWARNING, ("[[AVAudioSession sharedInstance] setActive]] error.", [err localizedDescription])); -} - - (vector>)availableLanguages { return _availableLanguages; @@ -142,6 +140,7 @@ using namespace locale_translator; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ { self.speechSynthesizer = [[AVSpeechSynthesizer alloc] init]; + self.speechSynthesizer.delegate = self; [self createVoice:self.savedLanguage]; }); // TODO(vbykoianko) Use [NSLocale preferredLanguages] instead of [AVSpeechSynthesisVoice currentLanguageCode]. @@ -195,7 +194,10 @@ using namespace locale_translator; - (void)speakOneString:(NSString *)textToSpeak { if (!textToSpeak || ![textToSpeak length]) + { + [self reduceNotificationsToSpeak]; return; + } NSLog(@"Speak text: %@", textToSpeak); AVSpeechUtterance * utterance = [AVSpeechUtterance speechUtteranceWithString:textToSpeak]; @@ -216,6 +218,14 @@ using namespace locale_translator; if (![self isValid]) return; + if (!notifications.empty()) + { + BOOL const shouldStartAudioSession = (self.notificationsToSpeak == 0); + self.notificationsToSpeak += notifications.size(); + if (shouldStartAudioSession && ![self setAudioSessionActive:YES]) + return; + } + for (auto const & text : notifications) [self speakOneString:@(text.c_str())]; } @@ -244,6 +254,36 @@ static vector> availableLanguages() return result; } +- (BOOL)setAudioSessionActive:(BOOL)audioSessionActive +{ + NSError * err; + if (![self.audioSession setActive:audioSessionActive withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&err]) + { + LOG(LWARNING, ("[[AVAudioSession sharedInstance] setActive]] error.", [err localizedDescription])); + return NO; + } + return YES; +} + +- (void)reduceNotificationsToSpeak +{ + self.notificationsToSpeak = MAX(self.notificationsToSpeak - 1, 0); + if (self.notificationsToSpeak == 0) + [self setAudioSessionActive:NO]; +} + +#pragma mark - AVSpeechSynthesizerDelegate + +- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didCancelSpeechUtterance:(AVSpeechUtterance *)utterance +{ + [self reduceNotificationsToSpeak]; +} + +- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance +{ + [self reduceNotificationsToSpeak]; +} + @end diff --git a/iphone/Maps/Classes/MapsAppDelegate.mm b/iphone/Maps/Classes/MapsAppDelegate.mm index 9548b699a5..13069001a7 100644 --- a/iphone/Maps/Classes/MapsAppDelegate.mm +++ b/iphone/Maps/Classes/MapsAppDelegate.mm @@ -426,7 +426,6 @@ using namespace osm_auth_ios; [self incrementSessionsCountAndCheckForAlert]; [self enableTTSForTheFirstTime]; - [MWMTextToSpeech activateAudioSession]; return returnValue; } @@ -586,7 +585,6 @@ using namespace osm_auth_ios; { LOG(LINFO, ("applicationWillEnterForeground")); GetFramework().EnterForeground(); - [MWMTextToSpeech activateAudioSession]; } - (void)applicationDidBecomeActive:(UIApplication *)application diff --git a/iphone/Maps/MAPSME.plist b/iphone/Maps/MAPSME.plist index dc06413ea5..d0014389f9 100644 --- a/iphone/Maps/MAPSME.plist +++ b/iphone/Maps/MAPSME.plist @@ -173,6 +173,7 @@ UIBackgroundModes + audio fetch location remote-notification