Skip to content

Commit 5d85a0a

Browse files
committed
feat: implement askForSpeechRecognitionAccess()
1 parent c9ec480 commit 5d85a0a

File tree

3 files changed

+78
-2
lines changed

3 files changed

+78
-2
lines changed

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,33 @@ askForCalendarAccess().then(status => {
100100
})
101101
```
102102

103+
## `permissions.askForSpeechRecognitionAccess()`
104+
105+
Returns `Promise<String>` - Whether or not the request succeeded or failed; can be `authorized` or `denied`.
106+
107+
Checks the authorization status for Speech Recognition access. If the status check returns:
108+
109+
* `not determined` - The Speech Recognition access authorization will prompt the user to authorize or deny. The Promise is resolved after the user selection with either `authorized` or `denied`.
110+
* `denied` - The `Security & Privacy` System Preferences window is opened with the Speech Recognition privacy key highlighted. On open of the `Security & Privacy` window, the Promise is resolved as `denied`.
111+
112+
Your app must provide an explanation for its use of Speech Recognition using the `NSSpeechRecognitionUsageDescription` `Info.plist` key;
113+
114+
```
115+
<key>NSSpeechRecognitionUsageDescription</key>
116+
<string>Your reason for wanting to access Speech Recognition</string>
117+
```
118+
119+
Example:
120+
```js
121+
const { askForSpeechRecognitionAccess } = require('node-mac-permissions')
122+
123+
askForSpeechRecognitionAccess().then(status => {
124+
console.log(`Access to Speech Recognition is ${status}`)
125+
})
126+
```
127+
128+
**Note:** `status` will be resolved back as `authorized` prior to macOS 10.15, where the underlying API was introduced.
129+
103130
## `permissions.askForRemindersAccess()`
104131

105132
Returns `Promise<String>` - Whether or not the request succeeded or failed; can be `authorized` or `denied`.

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ module.exports = {
3030
askForCameraAccess: permissions.askForCameraAccess,
3131
askForMicrophoneAccess: permissions.askForMicrophoneAccess,
3232
askForPhotosAccess: permissions.askForPhotosAccess,
33+
askForSpeechRecognitionAccess: permissions.askForSpeechRecognitionAccess,
3334
askForScreenCaptureAccess: permissions.askForScreenCaptureAccess,
3435
askForAccessibilityAccess: permissions.askForAccessibilityAccess,
3536
getAuthStatus,

permissions.mm

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ void AskForFullDiskAccess(const Napi::CallbackInfo &info) {
355355
Napi::Env env = info.Env();
356356
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
357357
Napi::ThreadSafeFunction ts_fn = Napi::ThreadSafeFunction::New(
358-
env, Napi::Function::New(env, NoOp), "cameraAccessCallback", 0, 1);
358+
env, Napi::Function::New(env, NoOp), "cameraCallback", 0, 1);
359359

360360
if (@available(macOS 10.14, *)) {
361361
std::string auth_status = MediaAuthStatus("camera");
@@ -395,6 +395,52 @@ void AskForFullDiskAccess(const Napi::CallbackInfo &info) {
395395
return deferred.Promise();
396396
}
397397

398+
// Request Speech Recognition access.
399+
Napi::Promise AskForSpeechRecognitionAccess(const Napi::CallbackInfo &info) {
400+
Napi::Env env = info.Env();
401+
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
402+
Napi::ThreadSafeFunction ts_fn = Napi::ThreadSafeFunction::New(
403+
env, Napi::Function::New(env, NoOp), "speechRecognitionCallback", 0, 1);
404+
405+
if (@available(macOS 10.15, *)) {
406+
std::string auth_status = SpeechRecognitionAuthStatus();
407+
408+
if (auth_status == "not determined") {
409+
__block Napi::ThreadSafeFunction tsfn = ts_fn;
410+
[SFSpeechRecognizer
411+
requestAuthorization:^(SFSpeechRecognizerAuthorizationStatus status) {
412+
auto callback = [=](Napi::Env env, Napi::Function js_cb,
413+
const char *granted) {
414+
deferred.Resolve(Napi::String::New(env, granted));
415+
};
416+
tsfn.BlockingCall(
417+
status == SFSpeechRecognizerAuthorizationStatusAuthorized
418+
? "authorized"
419+
: "denied",
420+
callback);
421+
tsfn.Release();
422+
}];
423+
} else if (auth_status == "denied") {
424+
NSWorkspace *workspace = [[NSWorkspace alloc] init];
425+
NSString *pref_string = @"x-apple.systempreferences:com.apple.preference."
426+
@"security?Privacy_SpeechRecognition";
427+
428+
[workspace openURL:[NSURL URLWithString:pref_string]];
429+
430+
ts_fn.Release();
431+
deferred.Resolve(Napi::String::New(env, "denied"));
432+
} else {
433+
ts_fn.Release();
434+
deferred.Resolve(Napi::String::New(env, auth_status));
435+
}
436+
} else {
437+
ts_fn.Release();
438+
deferred.Resolve(Napi::String::New(env, "authorized"));
439+
}
440+
441+
return deferred.Promise();
442+
}
443+
398444
// Request Photos access.
399445
Napi::Promise AskForPhotosAccess(const Napi::CallbackInfo &info) {
400446
Napi::Env env = info.Env();
@@ -443,7 +489,7 @@ void AskForFullDiskAccess(const Napi::CallbackInfo &info) {
443489
Napi::Env env = info.Env();
444490
Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
445491
Napi::ThreadSafeFunction ts_fn = Napi::ThreadSafeFunction::New(
446-
env, Napi::Function::New(env, NoOp), "microphoneAccessCallback", 0, 1);
492+
env, Napi::Function::New(env, NoOp), "microphoneCallback", 0, 1);
447493

448494
if (@available(macOS 10.14, *)) {
449495
std::string auth_status = MediaAuthStatus("microphone");
@@ -534,6 +580,8 @@ void AskForAccessibilityAccess(const Napi::CallbackInfo &info) {
534580
Napi::Function::New(env, AskForCameraAccess));
535581
exports.Set(Napi::String::New(env, "askForMicrophoneAccess"),
536582
Napi::Function::New(env, AskForMicrophoneAccess));
583+
exports.Set(Napi::String::New(env, "askForSpeechRecognitionAccess"),
584+
Napi::Function::New(env, AskForSpeechRecognitionAccess));
537585
exports.Set(Napi::String::New(env, "askForPhotosAccess"),
538586
Napi::Function::New(env, AskForPhotosAccess));
539587
exports.Set(Napi::String::New(env, "askForScreenCaptureAccess"),

0 commit comments

Comments
 (0)