Skip to content

Commit

Permalink
feat: add media access apis for mojave
Browse files Browse the repository at this point in the history
  • Loading branch information
codebytere committed Nov 7, 2018
1 parent bf3edf8 commit ad39da6
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 0 deletions.
1 change: 1 addition & 0 deletions BUILD.gn
Expand Up @@ -601,6 +601,7 @@ if (is_mac) {
sources = filenames.framework_sources

libs = [
"AVFoundation.framework",
"Carbon.framework",
"QuartzCore.framework",
"Quartz.framework",
Expand Down
4 changes: 4 additions & 0 deletions atom/browser/api/atom_api_system_preferences.cc
Expand Up @@ -89,6 +89,10 @@ void SystemPreferences::BuildPrototype(
&SystemPreferences::GetAppLevelAppearance)
.SetMethod("setAppLevelAppearance",
&SystemPreferences::SetAppLevelAppearance)
.SetMethod("hasCameraAccess", &SystemPreferences::HasCameraAccess)
.SetMethod("hasMicrophoneAccess", &SystemPreferences::HasMicrophoneAccess)
.SetMethod("hasFullMediaAccess", &SystemPreferences::HasFullMediaAccess)
.SetMethod("askForMediaAccess", &SystemPreferences::AskForMediaAccess)
#endif
.SetMethod("isInvertedColorScheme",
&SystemPreferences::IsInvertedColorScheme)
Expand Down
6 changes: 6 additions & 0 deletions atom/browser/api/atom_api_system_preferences.h
Expand Up @@ -90,6 +90,12 @@ class SystemPreferences : public mate::EventEmitter<SystemPreferences>
void RemoveUserDefault(const std::string& name);
bool IsSwipeTrackingFromScrollEventsEnabled();

bool HasCameraAccess();
bool HasMicrophoneAccess();
bool HasFullMediaAccess();

void AskForMediaAccess(mate::Arguments* args);

// TODO(MarshallOfSound): Write tests for these methods once we
// are running tests on a Mojave machine
v8::Local<v8::Value> GetEffectiveAppearance(v8::Isolate* isolate);
Expand Down
42 changes: 42 additions & 0 deletions atom/browser/api/atom_api_system_preferences_mac.mm
Expand Up @@ -4,12 +4,14 @@

#include "atom/browser/api/atom_api_system_preferences.h"

#include <iostream>
#include <map>

#import <Cocoa/Cocoa.h>

#include "atom/browser/mac/atom_application.h"
#include "atom/browser/mac/dict_util.h"
#include "atom/browser/ui/cocoa/atom_access_controller.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/native_mate_converters/value_converter.h"
#include "base/strings/sys_string_conversions.h"
Expand Down Expand Up @@ -360,6 +362,46 @@ static bool FromV8(v8::Isolate* isolate,
}
}

bool SystemPreferences::HasCameraAccess() {
return [[AtomAccessController sharedController] hasCameraAccess];
}

bool SystemPreferences::HasMicrophoneAccess() {
return [[AtomAccessController sharedController] hasMicrophoneAccess];
}

// whether the system has access to both microphone and camera
bool SystemPreferences::HasFullMediaAccess() {
return [[AtomAccessController sharedController] hasFullMediaAccess];
}

// ask for access to camera and/or microphone
void SystemPreferences::AskForMediaAccess(mate::Arguments* args) {
std::string media_type;
bool ask_again = false;
if (!args->GetNext(&ask_again))
args->ThrowError("Ask again value required");

if (args->GetNext(&media_type)) {
std::cout << media_type << "\n";
if (media_type == "microphone")
[[AtomAccessController sharedController] askForMicrophoneAccess];
else if (media_type == "camera")
[[AtomAccessController sharedController] askForCameraAccess];
else
args->ThrowError("Invalid media type");
} else {
[[AtomAccessController sharedController]
askForMediaAccess:ask_again
completion:^(BOOL granted) {
if (granted)
printf("Access granted!!\n");
else
printf("Access denied!\n");
}];
}
}

void SystemPreferences::RemoveUserDefault(const std::string& name) {
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults removeObjectForKey:base::SysUTF8ToNSString(name)];
Expand Down
35 changes: 35 additions & 0 deletions atom/browser/ui/cocoa/atom_access_controller.h
@@ -0,0 +1,35 @@
// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#import <Cocoa/Cocoa.h>

#ifndef ATOM_BROWSER_UI_COCOA_ATOM_ACCESS_CONTROLLER_H_
#define ATOM_BROWSER_UI_COCOA_ATOM_ACCESS_CONTROLLER_H_

typedef NS_ENUM(NSInteger, AccessState) {
AccessStateUnknown,
AccessStateGranted,
AccessStateDenied
};

@interface AtomAccessController : NSObject {
AccessState microphoneAccessState_;
AccessState cameraAccessState_;
}

+ (instancetype)sharedController;

- (void)askForMediaAccess:(BOOL)askAgain
completion:(void (^)(BOOL))allAccessGranted;

- (void)askForMicrophoneAccess;
- (void)askForCameraAccess;

- (BOOL)hasMicrophoneAccess;
- (BOOL)hasCameraAccess;
- (BOOL)hasFullMediaAccess;

@end

#endif // ATOM_BROWSER_UI_COCOA_ATOM_ACCESS_CONTROLLER_H_
143 changes: 143 additions & 0 deletions atom/browser/ui/cocoa/atom_access_controller.mm
@@ -0,0 +1,143 @@
#include "atom/browser/ui/cocoa/atom_access_controller.h"

#import <AVFoundation/AVFoundation.h>
#import <Cocoa/Cocoa.h>

@implementation AtomAccessController

+ (instancetype)sharedController {
static dispatch_once_t once;
static AtomAccessController* sharedController;
dispatch_once(&once, ^{
sharedController = [[self alloc] init];
});
return sharedController;
}

- (instancetype)init {
if ((self = [super init])) {
if (@available(macOS 10.14, *)) {
switch (
[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) {
case AVAuthorizationStatusAuthorized:
case AVAuthorizationStatusRestricted:
cameraAccessState_ = AccessStateDenied;
break;
case AVAuthorizationStatusDenied:
cameraAccessState_ = AccessStateDenied;
break;
case AVAuthorizationStatusNotDetermined:
cameraAccessState_ = AccessStateDenied;
}
switch (
[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]) {
case AVAuthorizationStatusAuthorized:
case AVAuthorizationStatusRestricted:
microphoneAccessState_ = AccessStateDenied;
break;
case AVAuthorizationStatusDenied:
microphoneAccessState_ = AccessStateDenied;
break;
case AVAuthorizationStatusNotDetermined:
microphoneAccessState_ = AccessStateDenied;
}
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserver:self
selector:@selector(applicationLaunched:)
name:NSWorkspaceDidLaunchApplicationNotification
object:nil];
} else {
cameraAccessState_ = AccessStateGranted;
microphoneAccessState_ = AccessStateDenied;
}
}
return self;
}

- (void)askForMicrophoneAccess {
if (microphoneAccessState_ == AccessStateDenied) {
NSAlert* alert = [[NSAlert alloc] init];
alert.alertStyle = NSAlertStyleWarning;
alert.messageText = @"This app needs access to the microphone.";
[alert addButtonWithTitle:@"Change Preferences"];
[alert addButtonWithTitle:@"Cancel"];
NSInteger modalResponse = [alert runModal];
if (modalResponse == NSAlertFirstButtonReturn) {
[[NSWorkspace sharedWorkspace]
openURL:[NSURL
URLWithString:@"x-apple.systempreferences:com.apple."
@"preference.security?Privacy_Microphone"]];
}
}
}

- (void)askForCameraAccess {
if (cameraAccessState_ == AccessStateDenied) {
NSAlert* alert = [[NSAlert alloc] init];
alert.alertStyle = NSAlertStyleWarning;
alert.messageText = @"This app needs access to the camera.";
[alert addButtonWithTitle:@"Change Preferences"];
[alert addButtonWithTitle:@"Cancel"];
NSInteger modalResponse = [alert runModal];
if (modalResponse == NSAlertFirstButtonReturn) {
[[NSWorkspace sharedWorkspace]
openURL:[NSURL URLWithString:@"x-apple.systempreferences:com.apple."
@"preference.security?Privacy_Camera"]];
}
}
}

- (void)askForMediaAccess:(BOOL)askAgain
completion:(void (^)(BOOL))allAccessGranted {
if (@available(macOS 10.14, *)) {
[AVCaptureDevice
requestAccessForMediaType:AVMediaTypeAudio
completionHandler:^(BOOL granted) {
microphoneAccessState_ =
(granted) ? AccessStateGranted : AccessStateDenied;
[AVCaptureDevice
requestAccessForMediaType:AVMediaTypeVideo
completionHandler:^(BOOL granted) {
cameraAccessState_ = (granted)
? AccessStateGranted
: AccessStateDenied;
if (askAgain) {
dispatch_async(dispatch_get_main_queue(), ^{
[self askForMicrophoneAccess];
[self askForCameraAccess];
});
}
dispatch_async(dispatch_get_main_queue(), ^{
allAccessGranted(self.hasFullMediaAccess);
});
}];
}];
} else {
allAccessGranted(self.hasFullMediaAccess);
}
}

- (BOOL)mediaConsentStatusKnown {
return ((microphoneAccessState_ != AccessStateUnknown) &&
(cameraAccessState_ != AccessStateUnknown));
}

- (BOOL)hasCameraAccess {
if (@available(macOS 10.14, *)) {
return (cameraAccessState_ == AccessStateGranted);
}
return YES;
}

- (BOOL)hasMicrophoneAccess {
if (@available(macOS 10.14, *)) {
return (microphoneAccessState_ == AccessStateGranted);
}
return YES;
}

- (BOOL)hasFullMediaAccess {
return (self.hasCameraAccess && self.hasMicrophoneAccess);
}

@end
2 changes: 2 additions & 0 deletions filenames.gni
Expand Up @@ -390,6 +390,8 @@ filenames = {
"atom/browser/ui/certificate_trust.h",
"atom/browser/ui/certificate_trust_mac.mm",
"atom/browser/ui/certificate_trust_win.cc",
"atom/browser/ui/cocoa/atom_access_controller.h",
"atom/browser/ui/cocoa/atom_access_controller.mm",
"atom/browser/ui/cocoa/atom_bundle_mover.h",
"atom/browser/ui/cocoa/atom_bundle_mover.mm",
"atom/browser/ui/cocoa/atom_menu_controller.h",
Expand Down

0 comments on commit ad39da6

Please sign in to comment.