[ios] Add test voice button in TTS settings

Signed-off-by: Fabian Wüthrich <me@fabwu.ch>
This commit is contained in:
Fabian Wüthrich 2024-06-27 21:37:19 -06:00 committed by Alexander Borsuk
parent 905bce9a40
commit a4c89930f9
8 changed files with 147 additions and 12 deletions

View file

@ -5888,7 +5888,7 @@
[pref_tts_test_voice_title]
comment = Settings «Route» category: «Test Voice Directions» title
tags = android
tags = android,ios
en = Test Voice Directions (TTS, Text-To-Speech)
ar = اختبار الاتجاهات الصوتية (TTS، تحويل النص إلى كلام)
az = Test səsli göstərişlər (TTS, Text-to-Speech)
@ -28771,7 +28771,7 @@
[app_tip_00]
comment = App Tip #00
tags = android
tags = android,ios
en = Thank you for using our community-built maps!
ar = شكرًا لك على استخدام الخرائط التي أنشأها مجتمعنا!
az = İcma tərəfindən yaradılmış xəritələrimizdən istifadə etdiyiniz üçün təşəkkür edirik!
@ -28815,7 +28815,7 @@
[app_tip_01]
comment = App tip #01
tags = android
tags = android,ios
en = With your donations and support, we can create the best maps in the World!
ar = بتبرعاتكم ودعمكم، يمكننا إنشاء أفضل الخرائط في العالم!
be = Дзякуючы вашым ахвяраванням і падтрымцы мы можам стварыць лепшыя мапы у свеце!
@ -28858,7 +28858,7 @@
[app_tip_02]
comment = App tip #02
tags = android
tags = android,ios
en = Do you like our app? Please donate to support the development! Don't like it yet? Please let us know why, and we will fix it!
ar = هل تحب التطبيق لدينا؟ يرجى التبرع لدعم التنمية! لا أحب ذلك حتى الآن؟ واسمحوا لنا أن نعرف، وسوف نقوم بإصلاحه!
be = Вам падабаецца наша прылада? Калі ласка, ахвяруйце, каб падтрымаць развіццё! Яшчэ не падабаецца? Калі ласка, напішыце, чаму, і мы гэта выправім!
@ -28901,7 +28901,7 @@
[app_tip_03]
comment = App tip #03
tags = android
tags = android,ios
en = If you know a software developer, you can ask him or her to implement a feature that you need.
ar = إذا كنت تعرف أحد مطوري البرامج، فيمكنك أن تطلب منه تنفيذ الميزة التي تحتاجها.
be = Калі вы ведаеце распрацоўшчыка праграмнага забеспячэння, вы можаце папрасіць яго рэалізаваць неабходную вам функцыю.
@ -28944,7 +28944,7 @@
[app_tip_04]
comment = App tip #04
tags = android
tags = android,ios
en = Do you know that you can long-tap any place on the map to select it?
ar = هل تعلم أنه يمكنك النقر لفترة طويلة على أي مكان على الخريطة لتحديده؟
be = Любое месца на карце можна абраць, калі патрымаць там палец на працягу секунды.
@ -28987,7 +28987,7 @@
[app_tip_05]
comment = App tip #05
tags = android
tags = android,ios
en = Did you know that your current location on the map can be selected?
ar = هل تعلم أنه يمكنك تحديد موقعك الحالي على الخريطة؟
be = А вы ведалі, што сваё месцазнаходжанне на мапе можна абраць?
@ -29030,7 +29030,7 @@
[app_tip_06]
comment = App tip #06
tags = android
tags = android,ios
en = You can help to translate our app into your language.
ar = يمكنك المساعدة في ترجمة تطبيقنا إلى لغتك.
be = Вы можаце дапамагчы перакласці нашу прыладу на вашу мову.
@ -29073,7 +29073,7 @@
[app_tip_07]
comment = App tip #07
tags = android
tags = android,ios
en = Our app is developed by a few enthusiasts and the community.
ar = تم تطوير تطبيقنا من قبل عدد قليل من المتحمسين والمجتمع.
be = Наша прылада распрацавана некалькімі энтузіястамі і супольнасцю.
@ -29116,7 +29116,7 @@
[app_tip_08]
comment = App tip #08
tags = android
tags = android,ios
en = You can easily fix and improve the map data.
ar = يمكنك بسهولة إصلاح وتحسين بيانات الخريطة.
be = Вы можаце лёгка выправіць і палепшыць дадзеныя карты.
@ -29159,7 +29159,7 @@
[app_tip_09]
comment = App tip #09
tags = android
tags = android,ios
en = Our main goal is to build fast, privacy-focused, easy-to-use maps that you will love.
ar = هدفنا الرئيسي هو إنشاء خرائط سريعة وسهلة الاستخدام تركز على الخصوصية والتي ستعجبك.
be = Наша галоўная мэта - ствараць хуткія, арыентаваныя на прыватнасць і простыя ў выкарыстанні карты, якія вам спадабаюцца.

View file

@ -18,6 +18,7 @@
- (void)setNotificationsLocale:(NSString *)locale;
- (void)playTurnNotifications:(NSArray<NSString *> *)turnNotifications;
- (void)playWarningSound;
- (void)play:(NSString *)text;
- (instancetype)init __attribute__((unavailable("call +tts instead")));
- (instancetype)copy __attribute__((unavailable("call +tts instead")));

View file

@ -279,6 +279,13 @@ using Observers = NSHashTable<Observer>;
return _audioPlayer;
}
- (void)play:(NSString *)text {
if (![self isValid])
[self createVoice:[[self class] savedLanguage]];
[self speakOneString:text];
}
#pragma mark - MWMNavigationDashboardObserver
- (void)onTTSStatusUpdated {

View file

@ -0,0 +1,10 @@
NS_ASSUME_NONNULL_BEGIN
@interface TTSTester : NSObject
- (void)playRandomTestString;
- (NSArray<NSString *> *)getTestStrings:(NSString *)language;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,58 @@
#import "TTSTester.h"
#include <CoreApi/Framework.h>
#include "LocaleTranslator.h"
#include "MWMTextToSpeech.h"
@implementation TTSTester
static NSString * const NotFoundDelimiter = @"__not_found__";
NSArray<NSString *> * testStrings;
NSString * testStringsLanguage;
int testStringIndex;
- (void)playRandomTestString {
NSString * currentTTSLanguage = MWMTextToSpeech.savedLanguage;
if (testStrings == nil || ![currentTTSLanguage isEqualToString:testStringsLanguage]) {
testStrings = [self getTestStrings:currentTTSLanguage];
if (testStrings == nil) {
LOG(LWARNING, ("Couldn't load TTS test strings"));
return;
}
testStringsLanguage = currentTTSLanguage;
}
[[MWMTextToSpeech tts] play:testStrings[testStringIndex]];
if (++testStringIndex >= testStrings.count)
testStringIndex = 0;
}
- (NSArray<NSString *> *)getTestStrings:(NSString *)language {
NSString * twineLanguage = [NSString stringWithUTF8String:locale_translator::bcp47ToTwineLanguage(language).c_str()];
NSString * languagePath = [NSBundle.mainBundle pathForResource:twineLanguage ofType:@"lproj"];
if (languagePath == nil) {
LOG(LWARNING, ("Couldn't find translation file for ", twineLanguage.UTF8String));
return nil;
}
NSBundle * bundle = [NSBundle bundleWithPath:languagePath];
NSMutableArray * appTips = [NSMutableArray new];
for (int idx = 0; ; idx++) {
NSString * appTipKey = [NSString stringWithFormat:@"app_tip_%02d", idx];
NSString * appTip = [bundle localizedStringForKey:appTipKey value:NotFoundDelimiter table:nil];
if ([appTip isEqualToString:NotFoundDelimiter])
break;
[appTips addObject:appTip];
}
// shuffle
for (NSUInteger i = appTips.count; i > 1; i--)
[appTips exchangeObjectAtIndex:i - 1 withObjectAtIndex:arc4random_uniform((u_int32_t)i)];
return appTips;
}
@end

View file

@ -261,6 +261,8 @@
47F86D0120C93D8D00FEE291 /* TabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47F86D0020C93D8D00FEE291 /* TabViewController.swift */; };
4A300ED51C6DCFD400140018 /* countries-strings in Resources */ = {isa = PBXBuildFile; fileRef = 4A300ED31C6DCFD400140018 /* countries-strings */; };
4B4153B52BF9695500EE4B02 /* MWMTextToSpeechTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B4153B42BF9695500EE4B02 /* MWMTextToSpeechTests.mm */; };
4B83AE492C2E59F800B0C3BC /* TTSTester.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B83AE482C2E59F800B0C3BC /* TTSTester.mm */; };
4B83AE4B2C2E642100B0C3BC /* TTSTesterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B83AE4A2C2E642100B0C3BC /* TTSTesterTest.m */; };
6741A9421BF340DE002C974C /* sound-strings in Resources */ = {isa = PBXBuildFile; fileRef = 5605022E1B6211E100169CAD /* sound-strings */; };
6741A9451BF340DE002C974C /* classificator.txt in Resources */ = {isa = PBXBuildFile; fileRef = EE026F0511D6AC0D00645242 /* classificator.txt */; };
6741A9491BF340DE002C974C /* countries.txt in Resources */ = {isa = PBXBuildFile; fileRef = FA46DA2B12D4166E00968C36 /* countries.txt */; };
@ -1186,6 +1188,9 @@
4A7D89C31B2EBF3B00AC843E /* resources-xhdpi_dark */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "resources-xhdpi_dark"; path = "../../data/resources-xhdpi_dark"; sourceTree = "<group>"; };
4A7D89C41B2EBF3B00AC843E /* resources-xxhdpi_dark */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "resources-xxhdpi_dark"; path = "../../data/resources-xxhdpi_dark"; sourceTree = "<group>"; };
4B4153B42BF9695500EE4B02 /* MWMTextToSpeechTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMTextToSpeechTests.mm; sourceTree = "<group>"; };
4B83AE472C2E59F800B0C3BC /* TTSTester.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TTSTester.h; sourceTree = "<group>"; };
4B83AE482C2E59F800B0C3BC /* TTSTester.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TTSTester.mm; sourceTree = "<group>"; };
4B83AE4A2C2E642100B0C3BC /* TTSTesterTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TTSTesterTest.m; sourceTree = "<group>"; };
5605022E1B6211E100169CAD /* sound-strings */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "sound-strings"; path = "../../data/sound-strings"; sourceTree = "<group>"; };
6741AA5D1BF340DE002C974C /* Organic Maps (Debug).app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Organic Maps (Debug).app"; sourceTree = BUILT_PRODUCTS_DIR; };
6B15907026623AE500944BBA /* 00_NotoSansThai-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "00_NotoSansThai-Regular.ttf"; path = "../../data/00_NotoSansThai-Regular.ttf"; sourceTree = "<group>"; };
@ -2289,6 +2294,8 @@
34763EE51F2F392300F4D2D3 /* MWMTextToSpeech.mm */,
34763EE91F2F394D00F4D2D3 /* MWMTextToSpeech+CPP.h */,
34763EEA1F2F3AD700F4D2D3 /* MWMTextToSpeechObserver.h */,
4B83AE472C2E59F800B0C3BC /* TTSTester.h */,
4B83AE482C2E59F800B0C3BC /* TTSTester.mm */,
);
path = TextToSpeech;
sourceTree = "<group>";
@ -2666,6 +2673,7 @@
4B4153B72BF970A000EE4B02 /* TextToSpeech */ = {
isa = PBXGroup;
children = (
4B83AE4A2C2E642100B0C3BC /* TTSTesterTest.m */,
4B4153B42BF9695500EE4B02 /* MWMTextToSpeechTests.mm */,
);
path = TextToSpeech;
@ -4477,6 +4485,7 @@
3404755F1E081A4600C92850 /* MWMLocationPredictor.mm in Sources */,
993DF11523F6BDB100AC231A /* UISearchBarRenderer.swift in Sources */,
F6E2FE041E097BA00083EBEC /* MWMOpeningHoursDaysSelectorTableViewCell.mm in Sources */,
4B83AE492C2E59F800B0C3BC /* TTSTester.mm in Sources */,
993DF12023F6BDB100AC231A /* TabViewRenderer.swift in Sources */,
F6E2FE131E097BA00083EBEC /* MWMOpeningHoursTimeSelectorTableViewCell.mm in Sources */,
F626D52F1C3E83F800C17D15 /* MWMTableViewCell.m in Sources */,
@ -4629,6 +4638,7 @@
EDF838C32C00B9D6007E4E67 /* UbiquitousDirectoryMonitorDelegateMock.swift in Sources */,
EDF838BE2C00B9D0007E4E67 /* LocalDirectoryMonitorDelegateMock.swift in Sources */,
EDF838BF2C00B9D0007E4E67 /* SynchronizationStateManagerTests.swift in Sources */,
4B83AE4B2C2E642100B0C3BC /* TTSTesterTest.m in Sources */,
EDF838C22C00B9D6007E4E67 /* MetadataItemStubs.swift in Sources */,
4B4153B52BF9695500EE4B02 /* MWMTextToSpeechTests.mm in Sources */,
EDF838C42C00B9D6007E4E67 /* FileManagerMock.swift in Sources */,

View file

@ -0,0 +1,28 @@
#import <XCTest/XCTest.h>
#import "TTSTester.h"
@interface TTSTesterTest : XCTestCase
@end
@implementation TTSTesterTest
TTSTester * ttsTester;
- (void)setUp {
ttsTester = [[TTSTester alloc] init];
}
- (void)testTestStringsWithEnglish {
XCTAssertTrue([[ttsTester getTestStrings:@"en-US"] containsObject: @"Thank you for using our community-built maps!"]);
}
- (void)testTestStringsWithGerman {
XCTAssertTrue([[ttsTester getTestStrings:@"de-DE"] containsObject: @"Danke, dass du unsere von der Community erstellten Karten benutzt!"]);
}
- (void)testTestStringsWithInvalidLanguage {
XCTAssertNil([ttsTester getTestStrings:@"xxx"]);
}
@end

View file

@ -1,6 +1,7 @@
#import "MWMTTSSettingsViewController.h"
#import <AVFoundation/AVFoundation.h>
#import "MWMTextToSpeech+CPP.h"
#import "TTSTester.h"
#import "SwiftBridge.h"
#include <CoreApi/Framework.h>
@ -63,10 +64,13 @@ struct VoiceInstructionCellStrategy : BaseCellStategy
struct LanguageCellStrategy : BaseCellStategy
{
TTSTester * ttsTester = [[TTSTester alloc] init];
UITableViewCell * BuildCell(UITableView * tableView, NSIndexPath * indexPath,
MWMTTSSettingsViewController * controller) override
{
NSInteger const row = indexPath.row;
// "Other" cell
if (row == controller.languages.size())
{
Class cls = [SettingsTableViewLinkCell class];
@ -75,6 +79,17 @@ struct LanguageCellStrategy : BaseCellStategy
[cell configWithTitle:L(@"pref_tts_other_section_title") info:nil];
return cell;
}
// "Test TTS" cell
if (row == controller.languages.size() + 1)
{
Class cls = [SettingsTableViewSelectableCell class];
auto cell = static_cast<SettingsTableViewSelectableCell *>(
[tableView dequeueReusableCellWithCellClass:cls indexPath:indexPath]);
[cell configWithTitle:L(@"pref_tts_test_voice_title")];
cell.accessoryType = UITableViewCellAccessoryNone;
return cell;
}
Class cls = [SettingsTableViewSelectableCell class];
auto cell = static_cast<SettingsTableViewSelectableCell *>(
@ -97,7 +112,7 @@ struct LanguageCellStrategy : BaseCellStategy
size_t NumberOfRows(MWMTTSSettingsViewController * controller) const override
{
return controller.languages.size() + 1; // Number of languages + "Other" cell
return controller.languages.size() + 2; // Number of languages + "Other" cell + "TTS Test" cell
}
NSString * TitleForHeader() const override { return L(@"pref_tts_language_title"); }
@ -112,6 +127,12 @@ struct LanguageCellStrategy : BaseCellStategy
return;
}
if (row == controller.languages.size() + 1)
{
[ttsTester playRandomTestString];
return;
}
auto cell = [tableView cellForRowAtIndexPath:indexPath];
if (m_selectedCell == cell)
return;