VoiceMessageTranscribeViewController.m 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /**
  2. * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. #import <Speech/Speech.h>
  6. #import "VoiceMessageTranscribeViewController.h"
  7. #import "NCAppBranding.h"
  8. @interface VoiceMessageTranscribeViewController () {
  9. UIActivityIndicatorView *_activityIndicator;
  10. NSURL *_audioFileUrl;
  11. NSArray *_supportedLocales;
  12. }
  13. @end
  14. @implementation VoiceMessageTranscribeViewController
  15. - (id)initWithAudiofileUrl:(NSURL *)audioFileUrl
  16. {
  17. self = [super init];
  18. if (self) {
  19. self->_audioFileUrl = audioFileUrl;
  20. }
  21. return self;
  22. }
  23. - (void)viewDidLoad {
  24. [super viewDidLoad];
  25. self.navigationItem.title = NSLocalizedString(@"Transcript", @"TRANSLATORS transcript of a voice-message");
  26. [self.navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName:[NCAppBranding themeTextColor]}];
  27. self.navigationController.navigationBar.tintColor = [NCAppBranding themeTextColor];
  28. self.navigationController.navigationBar.translucent = NO;
  29. self.navigationController.navigationBar.barTintColor = [NCAppBranding themeColor];
  30. UIColor *themeColor = [NCAppBranding themeColor];
  31. UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
  32. [appearance configureWithOpaqueBackground];
  33. appearance.backgroundColor = themeColor;
  34. appearance.titleTextAttributes = @{NSForegroundColorAttributeName:[NCAppBranding themeTextColor]};
  35. self.navigationItem.standardAppearance = appearance;
  36. self.navigationItem.compactAppearance = appearance;
  37. self.navigationItem.scrollEdgeAppearance = appearance;
  38. UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
  39. target:self action:@selector(closeViewController)];
  40. self.navigationController.navigationBar.topItem.leftBarButtonItem = cancelButton;
  41. _activityIndicator = [[UIActivityIndicatorView alloc] init];
  42. _activityIndicator.color = [NCAppBranding themeTextColor];
  43. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self->_activityIndicator];
  44. [_activityIndicator startAnimating];
  45. _supportedLocales = @[@"de", @"it", @"en", @"fr", @"es"];
  46. [self checkPermissionAndStartTranscription];
  47. }
  48. - (void)closeViewController
  49. {
  50. [self dismissViewControllerAnimated:YES completion:nil];
  51. }
  52. - (void)checkPermissionAndStartTranscription
  53. {
  54. [SFSpeechRecognizer requestAuthorization:^(SFSpeechRecognizerAuthorizationStatus status){
  55. dispatch_async(dispatch_get_main_queue(), ^{
  56. if (status != SFSpeechRecognizerAuthorizationStatusAuthorized) {
  57. [self showSpeechRecognitionNotAvailable];
  58. return;
  59. }
  60. [self showLocaleSelection];
  61. });
  62. }];
  63. }
  64. - (void)showLocaleSelection
  65. {
  66. UIAlertController *optionsActionSheet = [UIAlertController alertControllerWithTitle:nil
  67. message:nil
  68. preferredStyle:UIAlertControllerStyleActionSheet];
  69. // Use current locale for showing localized language names
  70. NSLocale *currentLocale = NSLocale.currentLocale;
  71. for (NSString *localeString in _supportedLocales) {
  72. NSLocale *speechLocale = [[NSLocale alloc] initWithLocaleIdentifier:localeString];
  73. SFSpeechRecognizer *speechRecognizer = [[SFSpeechRecognizer alloc] initWithLocale:speechLocale];
  74. if (!speechRecognizer.isAvailable || !speechRecognizer.supportsOnDeviceRecognition) {
  75. // We explicitly want to use on-device recognition
  76. continue;
  77. }
  78. UIAlertAction *localeAction = [UIAlertAction actionWithTitle:[currentLocale localizedStringForLanguageCode:localeString]
  79. style:UIAlertActionStyleDefault
  80. handler:^void (UIAlertAction *action) {
  81. [self transcribeWithLocale:speechLocale];
  82. }];
  83. [optionsActionSheet addAction:localeAction];
  84. }
  85. [optionsActionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil)
  86. style:UIAlertActionStyleCancel
  87. handler:^void (UIAlertAction *action) {
  88. [self closeViewController];
  89. }]];
  90. [self presentViewController:optionsActionSheet animated:YES completion:nil];
  91. }
  92. - (void)transcribeWithLocale:(NSLocale *)locale
  93. {
  94. SFSpeechRecognizer *speechRecognizer = [[SFSpeechRecognizer alloc] initWithLocale:locale];
  95. SFSpeechURLRecognitionRequest *speechRecognitionRequest = [[SFSpeechURLRecognitionRequest alloc] initWithURL:_audioFileUrl];
  96. speechRecognitionRequest.requiresOnDeviceRecognition = YES;
  97. speechRecognitionRequest.shouldReportPartialResults = YES;
  98. [speechRecognizer recognitionTaskWithRequest:speechRecognitionRequest
  99. resultHandler:^(SFSpeechRecognitionResult * _Nullable result, NSError * _Nullable error)
  100. {
  101. if (error) {
  102. NSLog(@"Recognition task failed: %@", error.description);
  103. [self showSpeechRecognitionError:error.localizedDescription];
  104. return;
  105. }
  106. NSString *transcribedText = result.bestTranscription.formattedString;
  107. [self setTranscribedText:transcribedText isFinal:result.final];
  108. }];
  109. }
  110. - (void)showSpeechRecognitionError:(NSString *)errorDescription
  111. {
  112. UIAlertController * alert = [UIAlertController
  113. alertControllerWithTitle:NSLocalizedString(@"Speech recognition failed", nil)
  114. message:errorDescription
  115. preferredStyle:UIAlertControllerStyleAlert];
  116. UIAlertAction* okButton = [UIAlertAction
  117. actionWithTitle:NSLocalizedString(@"OK", nil)
  118. style:UIAlertActionStyleDefault
  119. handler:^(UIAlertAction * _Nonnull action) {
  120. [self closeViewController];
  121. }];
  122. [alert addAction:okButton];
  123. [self presentViewController:alert animated:YES completion:nil];
  124. }
  125. - (void)showSpeechRecognitionNotAvailable
  126. {
  127. UIAlertController * alert = [UIAlertController
  128. alertControllerWithTitle:NSLocalizedString(@"Could not access speech recognition", nil)
  129. message:NSLocalizedString(@"Speech recognition access is not allowed. Check your settings.", nil)
  130. preferredStyle:UIAlertControllerStyleAlert];
  131. UIAlertAction* settingsButton = [UIAlertAction actionWithTitle:NSLocalizedString(@"Settings", nil)
  132. style:UIAlertActionStyleDefault
  133. handler:^(UIAlertAction * _Nonnull action) {
  134. [self closeViewController];
  135. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
  136. }];
  137. [alert addAction:settingsButton];
  138. [alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil)
  139. style:UIAlertActionStyleCancel
  140. handler:^(UIAlertAction * _Nonnull action) {
  141. [self closeViewController];
  142. }]];
  143. [self presentViewController:alert animated:YES completion:nil];
  144. }
  145. - (void)setTranscribedText:(NSString *)text isFinal:(BOOL)isFinal
  146. {
  147. [self.transcribeTextView setText:text];
  148. if (isFinal) {
  149. [_activityIndicator stopAnimating];
  150. [_activityIndicator removeFromSuperview];
  151. }
  152. }
  153. @end