feat: add media access APIs for macOS Mojave (#15624)

This commit is contained in:
Shelley Vohr 2018-12-04 07:54:13 -08:00 committed by GitHub
parent aa2b2f7c8f
commit c31629ad98
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 121 additions and 2 deletions

View file

@ -595,6 +595,7 @@ if (is_mac) {
sources = filenames.framework_sources sources = filenames.framework_sources
libs = [ libs = [
"AVFoundation.framework",
"Carbon.framework", "Carbon.framework",
"QuartzCore.framework", "QuartzCore.framework",
"Quartz.framework", "Quartz.framework",

View file

@ -89,6 +89,9 @@ void SystemPreferences::BuildPrototype(
&SystemPreferences::GetAppLevelAppearance) &SystemPreferences::GetAppLevelAppearance)
.SetMethod("setAppLevelAppearance", .SetMethod("setAppLevelAppearance",
&SystemPreferences::SetAppLevelAppearance) &SystemPreferences::SetAppLevelAppearance)
.SetMethod("getMediaAccessStatus",
&SystemPreferences::GetMediaAccessStatus)
.SetMethod("askForMediaAccess", &SystemPreferences::AskForMediaAccess)
#endif #endif
.SetMethod("isInvertedColorScheme", .SetMethod("isInvertedColorScheme",
&SystemPreferences::IsInvertedColorScheme) &SystemPreferences::IsInvertedColorScheme)

View file

@ -9,6 +9,7 @@
#include <string> #include <string>
#include "atom/browser/api/event_emitter.h" #include "atom/browser/api/event_emitter.h"
#include "atom/common/promise_util.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/values.h" #include "base/values.h"
#include "native_mate/handle.h" #include "native_mate/handle.h"
@ -90,6 +91,13 @@ class SystemPreferences : public mate::EventEmitter<SystemPreferences>
void RemoveUserDefault(const std::string& name); void RemoveUserDefault(const std::string& name);
bool IsSwipeTrackingFromScrollEventsEnabled(); bool IsSwipeTrackingFromScrollEventsEnabled();
// TODO(codebytere): Write tests for these methods once we
// are running tests on a Mojave machine
std::string GetMediaAccessStatus(const std::string& media_type,
mate::Arguments* args);
v8::Local<v8::Promise> AskForMediaAccess(v8::Isolate* isolate,
const std::string& media_type);
// TODO(MarshallOfSound): Write tests for these methods once we // TODO(MarshallOfSound): Write tests for these methods once we
// are running tests on a Mojave machine // are running tests on a Mojave machine
v8::Local<v8::Value> GetEffectiveAppearance(v8::Isolate* isolate); v8::Local<v8::Value> GetEffectiveAppearance(v8::Isolate* isolate);

View file

@ -6,6 +6,7 @@
#include <map> #include <map>
#import <AVFoundation/AVFoundation.h>
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#include "atom/browser/mac/atom_application.h" #include "atom/browser/mac/atom_application.h"
@ -78,6 +79,31 @@ int g_next_id = 0;
// The map to convert |id| to |int|. // The map to convert |id| to |int|.
std::map<int, id> g_id_map; std::map<int, id> g_id_map;
AVMediaType ParseMediaType(const std::string& media_type) {
if (media_type == "camera") {
return AVMediaTypeVideo;
} else if (media_type == "microphone") {
return AVMediaTypeAudio;
} else {
return nil;
}
}
std::string ConvertAuthorizationStatus(AVAuthorizationStatusMac status) {
switch (status) {
case AVAuthorizationStatusNotDeterminedMac:
return "not-determined";
case AVAuthorizationStatusRestrictedMac:
return "restricted";
case AVAuthorizationStatusDeniedMac:
return "denied";
case AVAuthorizationStatusAuthorizedMac:
return "granted";
default:
return "unknown";
}
}
} // namespace } // namespace
void SystemPreferences::PostNotification( void SystemPreferences::PostNotification(
@ -360,6 +386,47 @@ void SystemPreferences::SetUserDefault(const std::string& name,
} }
} }
std::string SystemPreferences::GetMediaAccessStatus(
const std::string& media_type,
mate::Arguments* args) {
if (auto type = ParseMediaType(media_type)) {
if (@available(macOS 10.14, *)) {
return ConvertAuthorizationStatus(
[AVCaptureDevice authorizationStatusForMediaType:type]);
} else {
// access always allowed pre-10.14 Mojave
return ConvertAuthorizationStatus(AVAuthorizationStatusAuthorizedMac);
}
} else {
args->ThrowError("Invalid media type");
return std::string();
}
}
v8::Local<v8::Promise> SystemPreferences::AskForMediaAccess(
v8::Isolate* isolate,
const std::string& media_type) {
scoped_refptr<util::Promise> promise = new util::Promise(isolate);
if (auto type = ParseMediaType(media_type)) {
if (@available(macOS 10.14, *)) {
[AVCaptureDevice requestAccessForMediaType:type
completionHandler:^(BOOL granted) {
dispatch_async(dispatch_get_main_queue(), ^{
promise->Resolve(!!granted);
});
}];
} else {
// access always allowed pre-10.14 Mojave
promise->Resolve(true);
}
} else {
promise->RejectWithErrorMessage("Invalid media type");
}
return promise->GetHandle();
}
void SystemPreferences::RemoveUserDefault(const std::string& name) { void SystemPreferences::RemoveUserDefault(const std::string& name) {
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults removeObjectForKey:base::SysUTF8ToNSString(name)]; [defaults removeObjectForKey:base::SysUTF8ToNSString(name)];

View file

@ -6,7 +6,9 @@
#include "base/mac/scoped_nsobject.h" #include "base/mac/scoped_nsobject.h"
#include "base/mac/scoped_sending_event.h" #include "base/mac/scoped_sending_event.h"
// Forward Declare Appareance APIs #import <AVFoundation/AVFoundation.h>
// Forward Declare Appearance APIs
@interface NSApplication (HighSierraSDK) @interface NSApplication (HighSierraSDK)
@property(copy, readonly) @property(copy, readonly)
NSAppearance* effectiveAppearance API_AVAILABLE(macosx(10.14)); NSAppearance* effectiveAppearance API_AVAILABLE(macosx(10.14));
@ -14,6 +16,27 @@
- (void)setAppearance:(NSAppearance*)appearance API_AVAILABLE(macosx(10.14)); - (void)setAppearance:(NSAppearance*)appearance API_AVAILABLE(macosx(10.14));
@end @end
// forward declare Access APIs
typedef NSString* AVMediaType NS_EXTENSIBLE_STRING_ENUM;
AVF_EXPORT AVMediaType const AVMediaTypeVideo;
AVF_EXPORT AVMediaType const AVMediaTypeAudio;
typedef NS_ENUM(NSInteger, AVAuthorizationStatusMac) {
AVAuthorizationStatusNotDeterminedMac = 0,
AVAuthorizationStatusRestrictedMac = 1,
AVAuthorizationStatusDeniedMac = 2,
AVAuthorizationStatusAuthorizedMac = 3,
};
@interface AVCaptureDevice (MojaveSDK)
+ (void)requestAccessForMediaType:(AVMediaType)mediaType
completionHandler:(void (^)(BOOL granted))handler
API_AVAILABLE(macosx(10.14));
+ (AVAuthorizationStatusMac)authorizationStatusForMediaType:
(AVMediaType)mediaType API_AVAILABLE(macosx(10.14));
@end
extern "C" { extern "C" {
#if !defined(MAC_OS_X_VERSION_10_14) || \ #if !defined(MAC_OS_X_VERSION_10_14) || \
MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_14 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_14

View file

@ -311,7 +311,6 @@ using `electron-packager` or `electron-forge` just set the `enableDarwinDarkMode
packager option to `true`. See the [Electron Packager API](https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#darwindarkmodesupport) packager option to `true`. See the [Electron Packager API](https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#darwindarkmodesupport)
for more details. for more details.
### `systemPreferences.getAppLevelAppearance()` _macOS_ ### `systemPreferences.getAppLevelAppearance()` _macOS_
Returns `String` | `null` - Can be `dark`, `light` or `unknown`. Returns `String` | `null` - Can be `dark`, `light` or `unknown`.
@ -326,3 +325,21 @@ You can use the `setAppLevelAppearance` API to set this value.
Sets the appearance setting for your application, this should override the Sets the appearance setting for your application, this should override the
system default and override the value of `getEffectiveAppearance`. system default and override the value of `getEffectiveAppearance`.
### `systemPreferences.getMediaAccessStatus(mediaType)` _macOS_
* `mediaType` String - `microphone` or `camera`.
Returns `String` - Can be `not-determined`, `granted`, `denied`, `restricted` or `unknown`.
This user consent was not required until macOS 10.14 Mojave, so this method will always return `granted` if your system is running 10.13 High Sierra or lower.
### `systemPreferences.askForMediaAccess(mediaType)` _macOS_
* `mediaType` String - the type of media being requested; can be `microphone`, `camera`.
Returns `Promise<Boolean>` - A promise that resolves with `true` if consent was granted and `false` if it was denied. If an invalid `mediaType` is passed, the promise will be rejected. If an access request was denied and later is changed through the System Preferences pane, a restart of the app will be required for the new permissions to take effect. If access has already been requested and denied, it _must_ be changed through the preference pane; an alert will not pop up and the promise will resolve with the existing access status.
**Important:** In order to properly leverage this API, you [must set](https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/requesting_authorization_for_media_capture_on_macos?language=objc) the `NSMicrophoneUsageDescription` and `NSCameraUsageDescription` strings in your app's `Info.plist` file. The values for these keys will be used to populate the permission dialogs so that the user will be properly informed as to the purpose of the permission request. See [Electron Application Distribution](https://electronjs.org/docs/tutorial/application-distribution#macos) for more information about how to set these in the context of Electron.
This user consent was not required until macOS 10.14 Mojave, so this method will always return `true` if your system is running 10.13 High Sierra or lower.