From 2dd231e15ae8ccbd5067738e349e32ccddc1b73b Mon Sep 17 00:00:00 2001 From: Alexey Zagarin Date: Fri, 19 Oct 2018 11:05:34 +0700 Subject: [PATCH] Fix `hasPermission()` always returning success on iOS - Use `[AVAudioSession sharedInstance] recordPermission` to check mic permissions Closes https://github.com/pbakondy/cordova-plugin-speechrecognition/issues/89 --- src/ios/SpeechRecognition.m | 172 ++++++++++++++++++++---------------- 1 file changed, 98 insertions(+), 74 deletions(-) diff --git a/src/ios/SpeechRecognition.m b/src/ios/SpeechRecognition.m index ef3553b..a0c34c9 100644 --- a/src/ios/SpeechRecognition.m +++ b/src/ios/SpeechRecognition.m @@ -15,6 +15,7 @@ #define MESSAGE_RESTRICTED @"Speech recognition restricted on this device" #define MESSAGE_NOT_DETERMINED @"Speech recognition not determined on this device" #define MESSAGE_ACCESS_DENIED_MICROPHONE @"User denied access to microphone" +#define MESSAGE_NOT_DETERMINED_MICROPHONE @"Microphone access not determined on this device" #define MESSAGE_ONGOING @"Ongoing speech recognition" @interface SpeechRecognition() @@ -58,100 +59,97 @@ - (void)startListening:(CDVInvokedUrlCommand*)command { return; } - [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted){ - if (!granted) { - NSLog(@"startListening() microphone access not authorized"); - CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:MESSAGE_ACCESS_DENIED_MICROPHONE]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; - return; - } + AVAudioSessionRecordPermission micStatus = [[AVAudioSession sharedInstance] recordPermission]; + if (micStatus != AVAudioSessionRecordPermissionGranted) { + NSLog(@"startListening() microphone access not authorized"); + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:MESSAGE_MISSING_PERMISSION]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } - NSString* language = [command argumentAtIndex:0 withDefault:DEFAULT_LANGUAGE]; - int matches = [[command argumentAtIndex:1 withDefault:@(DEFAULT_MATCHES)] intValue]; - BOOL showPartial = [[command argumentAtIndex:3 withDefault:@(NO)] boolValue]; + NSString* language = [command argumentAtIndex:0 withDefault:DEFAULT_LANGUAGE]; + int matches = [[command argumentAtIndex:1 withDefault:@(DEFAULT_MATCHES)] intValue]; + BOOL showPartial = [[command argumentAtIndex:3 withDefault:@(NO)] boolValue]; - NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:language]; - self.speechRecognizer = [[SFSpeechRecognizer alloc] initWithLocale:locale]; - self.audioEngine = [[AVAudioEngine alloc] init]; + NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:language]; + self.speechRecognizer = [[SFSpeechRecognizer alloc] initWithLocale:locale]; + self.audioEngine = [[AVAudioEngine alloc] init]; - // Cancel the previous task if it's running. - if ( self.recognitionTask ) { - [self.recognitionTask cancel]; - self.recognitionTask = nil; - } + // Cancel the previous task if it's running. + if ( self.recognitionTask ) { + [self.recognitionTask cancel]; + self.recognitionTask = nil; + } - AVAudioSession *audioSession = [AVAudioSession sharedInstance]; - [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil]; - [audioSession setMode:AVAudioSessionModeDefault error:nil]; - [audioSession setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil]; + AVAudioSession *audioSession = [AVAudioSession sharedInstance]; + [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil]; + [audioSession setMode:AVAudioSessionModeDefault error:nil]; + [audioSession setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil]; - self.recognitionRequest = [[SFSpeechAudioBufferRecognitionRequest alloc] init]; - self.recognitionRequest.shouldReportPartialResults = showPartial; + self.recognitionRequest = [[SFSpeechAudioBufferRecognitionRequest alloc] init]; + self.recognitionRequest.shouldReportPartialResults = showPartial; - AVAudioInputNode *inputNode = self.audioEngine.inputNode; - AVAudioFormat *format = [inputNode outputFormatForBus:0]; + AVAudioInputNode *inputNode = self.audioEngine.inputNode; + AVAudioFormat *format = [inputNode outputFormatForBus:0]; - self.recognitionTask = [self.speechRecognizer recognitionTaskWithRequest:self.recognitionRequest resultHandler:^(SFSpeechRecognitionResult *result, NSError *error) { + self.recognitionTask = [self.speechRecognizer recognitionTaskWithRequest:self.recognitionRequest resultHandler:^(SFSpeechRecognitionResult *result, NSError *error) { - if ( result ) { + if ( result ) { - NSMutableArray *resultArray = [[NSMutableArray alloc] init]; + NSMutableArray *resultArray = [[NSMutableArray alloc] init]; - int counter = 0; - for ( SFTranscription *transcription in result.transcriptions ) { - if (matches > 0 && counter < matches) { - [resultArray addObject:transcription.formattedString]; - } - counter++; + int counter = 0; + for ( SFTranscription *transcription in result.transcriptions ) { + if (matches > 0 && counter < matches) { + [resultArray addObject:transcription.formattedString]; } + counter++; + } - NSArray *transcriptions = [NSArray arrayWithArray:resultArray]; + NSArray *transcriptions = [NSArray arrayWithArray:resultArray]; - NSLog(@"startListening() recognitionTask result array: %@", transcriptions.description); + NSLog(@"startListening() recognitionTask result array: %@", transcriptions.description); - CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:transcriptions]; - if (showPartial){ - [pluginResult setKeepCallbackAsBool:YES]; - } - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:transcriptions]; + if (showPartial){ + [pluginResult setKeepCallbackAsBool:YES]; } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } - if ( error ) { - NSLog(@"startListening() recognitionTask error: %@", error.description); + if ( error ) { + NSLog(@"startListening() recognitionTask error: %@", error.description); - [self.audioEngine stop]; - [self.audioEngine.inputNode removeTapOnBus:0]; + [self.audioEngine stop]; + [self.audioEngine.inputNode removeTapOnBus:0]; - self.recognitionRequest = nil; - self.recognitionTask = nil; + self.recognitionRequest = nil; + self.recognitionTask = nil; - CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.description]; - if (showPartial){ - [pluginResult setKeepCallbackAsBool:YES]; - } - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.description]; + if (showPartial){ + [pluginResult setKeepCallbackAsBool:YES]; } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } - if ( result.isFinal ) { - NSLog(@"startListening() recognitionTask isFinal"); - - [self.audioEngine stop]; - [self.audioEngine.inputNode removeTapOnBus:0]; + if ( result.isFinal ) { + NSLog(@"startListening() recognitionTask isFinal"); - self.recognitionRequest = nil; - self.recognitionTask = nil; - } - }]; - - [inputNode installTapOnBus:0 bufferSize:1024 format:format block:^(AVAudioPCMBuffer *buffer, AVAudioTime *when) { - [self.recognitionRequest appendAudioPCMBuffer:buffer]; - }]; + [self.audioEngine stop]; + [self.audioEngine.inputNode removeTapOnBus:0]; - [self.audioEngine prepare]; - [self.audioEngine startAndReturnError:nil]; + self.recognitionRequest = nil; + self.recognitionTask = nil; + } + }]; + [inputNode installTapOnBus:0 bufferSize:1024 format:format block:^(AVAudioPCMBuffer *buffer, AVAudioTime *when) { + [self.recognitionRequest appendAudioPCMBuffer:buffer]; }]; + [self.audioEngine prepare]; + [self.audioEngine startAndReturnError:nil]; } - (void)stopListening:(CDVInvokedUrlCommand*)command { @@ -183,18 +181,44 @@ - (void)getSupportedLanguages:(CDVInvokedUrlCommand*)command { - (void)hasPermission:(CDVInvokedUrlCommand*)command { SFSpeechRecognizerAuthorizationStatus status = [SFSpeechRecognizer authorizationStatus]; - BOOL speechAuthGranted = (status == SFSpeechRecognizerAuthorizationStatusAuthorized); + CDVPluginResult *pluginResult = nil; + BOOL speechAuthGranted = NO; + + switch (status) { + case SFSpeechRecognizerAuthorizationStatusAuthorized: + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + speechAuthGranted = YES; + break; + case SFSpeechRecognizerAuthorizationStatusDenied: + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:MESSAGE_ACCESS_DENIED]; + break; + case SFSpeechRecognizerAuthorizationStatusRestricted: + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:MESSAGE_RESTRICTED]; + break; + case SFSpeechRecognizerAuthorizationStatusNotDetermined: + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:MESSAGE_NOT_DETERMINED]; + break; + } if (!speechAuthGranted) { - CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:NO]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; return; } - [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted){ - CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:granted]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; - }]; + AVAudioSessionRecordPermission micStatus = [[AVAudioSession sharedInstance] recordPermission]; + + switch (micStatus) { + case AVAudioSessionRecordPermissionGranted: + break; + case AVAudioSessionRecordPermissionDenied: + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:MESSAGE_ACCESS_DENIED_MICROPHONE]; + break; + case AVAudioSessionRecordPermissionUndetermined: + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:MESSAGE_NOT_DETERMINED_MICROPHONE]; + break; + } + + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } - (void)requestPermission:(CDVInvokedUrlCommand*)command {