Merge branch 'charlesmchen/permissionsVsInactive'

pull/1/head
Matthew Chen 8 years ago
commit b6ca9a8cef

@ -373,7 +373,7 @@
E1370BE518A0686C00826894 /* r.caf in Resources */ = {isa = PBXBuildFile; fileRef = E18AB40C18A05754001A532A /* r.caf */; };
E1370BE618A0686C00826894 /* sonarping.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = E18AB40D18A05754001A532A /* sonarping.mp3 */; };
E32B0699162419B7046BC643 /* libPods-Signal.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DB8EE72F8522189E3E2CB45 /* libPods-Signal.a */; };
EF764C351DB67CC5000D9A87 /* UIViewController+CameraPermissions.m in Sources */ = {isa = PBXBuildFile; fileRef = EF764C341DB67CC5000D9A87 /* UIViewController+CameraPermissions.m */; };
EF764C351DB67CC5000D9A87 /* UIViewController+Permissions.m in Sources */ = {isa = PBXBuildFile; fileRef = EF764C341DB67CC5000D9A87 /* UIViewController+Permissions.m */; };
FC3BD9881A30A790005B96BB /* Social.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC3BD9871A30A790005B96BB /* Social.framework */; };
FC5CDF391A3393DD00B47253 /* error_white@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FC5CDF371A3393DD00B47253 /* error_white@2x.png */; };
FC5CDF3A1A3393DD00B47253 /* warning_white@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FC5CDF381A3393DD00B47253 /* warning_white@2x.png */; };
@ -870,8 +870,8 @@
E18AB40D18A05754001A532A /* sonarping.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = sonarping.mp3; sourceTree = "<group>"; };
E1A0AD8B16E13FDD0071E604 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
E85DB184824BA9DC302EC8B3 /* Pods-SignalTests.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalTests.app store release.xcconfig"; path = "Pods/Target Support Files/Pods-SignalTests/Pods-SignalTests.app store release.xcconfig"; sourceTree = "<group>"; };
EF764C331DB67CC5000D9A87 /* UIViewController+CameraPermissions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIViewController+CameraPermissions.h"; path = "util/UIViewController+CameraPermissions.h"; sourceTree = "<group>"; };
EF764C341DB67CC5000D9A87 /* UIViewController+CameraPermissions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIViewController+CameraPermissions.m"; path = "util/UIViewController+CameraPermissions.m"; sourceTree = "<group>"; };
EF764C331DB67CC5000D9A87 /* UIViewController+Permissions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIViewController+Permissions.h"; path = "util/UIViewController+Permissions.h"; sourceTree = "<group>"; };
EF764C341DB67CC5000D9A87 /* UIViewController+Permissions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIViewController+Permissions.m"; path = "util/UIViewController+Permissions.m"; sourceTree = "<group>"; };
FC3BD9871A30A790005B96BB /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Social.framework; path = System/Library/Frameworks/Social.framework; sourceTree = SDKROOT; };
FC5CDF371A3393DD00B47253 /* error_white@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "error_white@2x.png"; sourceTree = "<group>"; };
FC5CDF381A3393DD00B47253 /* warning_white@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "warning_white@2x.png"; sourceTree = "<group>"; };
@ -1766,24 +1766,24 @@
FCFA64B11A24F29E0007FB87 /* UI Categories */ = {
isa = PBXGroup;
children = (
45638BDE1F3DDB2200128435 /* MessageSender+Promise.swift */,
450449371F45EE7D002D1ADA /* NSString+OWS.h */,
450449381F45EE7D002D1ADA /* NSString+OWS.m */,
45C0DC1A1E68FE9000E04C47 /* UIApplication+OWS.swift */,
FCFA64B21A24F3880007FB87 /* UIColor+OWS.h */,
FCFA64B31A24F3880007FB87 /* UIColor+OWS.m */,
45BB93371E688E14001E3939 /* UIDevice+featureSupport.swift */,
FCFA64B51A24F6730007FB87 /* UIFont+OWS.h */,
FCFA64B61A24F6730007FB87 /* UIFont+OWS.m */,
B68112E81A4D9EC400BA82FF /* UIImage+OWS.h */,
B68112E91A4D9EC400BA82FF /* UIImage+OWS.m */,
45C0DC1D1E69011F00E04C47 /* UIStoryboard+OWS.swift */,
34535D801E256BE9008A4747 /* UIView+OWS.h */,
34535D811E256BE9008A4747 /* UIView+OWS.m */,
EF764C331DB67CC5000D9A87 /* UIViewController+CameraPermissions.h */,
EF764C341DB67CC5000D9A87 /* UIViewController+CameraPermissions.m */,
344F2F651E57A932000D9322 /* UIViewController+OWS.h */,
344F2F661E57A932000D9322 /* UIViewController+OWS.m */,
45BB93371E688E14001E3939 /* UIDevice+featureSupport.swift */,
45C0DC1A1E68FE9000E04C47 /* UIApplication+OWS.swift */,
45C0DC1D1E69011F00E04C47 /* UIStoryboard+OWS.swift */,
45638BDE1F3DDB2200128435 /* MessageSender+Promise.swift */,
450449371F45EE7D002D1ADA /* NSString+OWS.h */,
450449381F45EE7D002D1ADA /* NSString+OWS.m */,
EF764C331DB67CC5000D9A87 /* UIViewController+Permissions.h */,
EF764C341DB67CC5000D9A87 /* UIViewController+Permissions.m */,
);
name = "UI Categories";
path = ..;
@ -2213,7 +2213,7 @@
4505C2BF1E648EA300CEBF41 /* ExperienceUpgrade.swift in Sources */,
34CCAF381F0C0599004084F4 /* AppUpdateNag.m in Sources */,
45387B041E36D650005D00B3 /* OWS102MoveLoggingPreferenceToUserDefaults.m in Sources */,
EF764C351DB67CC5000D9A87 /* UIViewController+CameraPermissions.m in Sources */,
EF764C351DB67CC5000D9A87 /* UIViewController+Permissions.m in Sources */,
45CD81EF1DC030E7004C9430 /* AccountManager.swift in Sources */,
45794E861E00620000066731 /* CallUIAdapter.swift in Sources */,
4585C4681ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift in Sources */,

@ -39,6 +39,7 @@
#import "UIImage+OWS.h"
#import "UIUtil.h"
#import "UIView+OWS.h"
#import "UIViewController+Permissions.h"
#import "ViewControllerUtils.h"
#import <AxolotlKit/NSData+keyVersionByte.h>
#import <Foundation/Foundation.h>

@ -4,8 +4,8 @@
#import "AttachmentSharing.h"
#import "TSAttachmentStream.h"
#import "Threading.h"
#import "UIUtil.h"
#import <SignalServiceKit/Threading.h>
@implementation AttachmentSharing

@ -48,8 +48,8 @@
#import "ThreadUtil.h"
#import "UIFont+OWS.h"
#import "UIUtil.h"
#import "UIViewController+CameraPermissions.h"
#import "UIViewController+OWS.h"
#import "UIViewController+Permissions.h"
#import "ViewControllerUtils.h"
#import <AVFoundation/AVFoundation.h>
#import <AddressBookUI/AddressBookUI.h>
@ -2473,7 +2473,10 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
- (void)takePictureOrVideo
{
[self ows_askForCameraPermissions:^{
[self ows_askForCameraPermissions:^(BOOL granted) {
if (!granted) {
return;
}
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.mediaTypes = @[ (__bridge NSString *)kUTTypeImage, (__bridge NSString *)kUTTypeMovie ];
@ -3048,27 +3051,25 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
self.voiceMessageUUID = voiceMessageUUID;
__weak typeof(self) weakSelf = self;
[[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(self) strongSelf = weakSelf;
if (!strongSelf) {
return;
}
[self ows_askForMicrophonePermissions:^(BOOL granted) {
__strong typeof(self) strongSelf = weakSelf;
if (!strongSelf) {
return;
}
if (strongSelf.voiceMessageUUID != voiceMessageUUID) {
// This voice message recording has been cancelled
// before recording could begin.
return;
}
if (strongSelf.voiceMessageUUID != voiceMessageUUID) {
// This voice message recording has been cancelled
// before recording could begin.
return;
}
if (granted) {
[strongSelf startRecordingVoiceMemo];
} else {
DDLogInfo(@"%@ we do not have recording permission.", self.logTag);
[strongSelf cancelVoiceMemo];
[OWSAlerts showNoMicrophonePermissionAlert];
}
});
if (granted) {
[strongSelf startRecordingVoiceMemo];
} else {
DDLogInfo(@"%@ we do not have recording permission.", self.logTag);
[strongSelf cancelVoiceMemo];
[OWSAlerts showNoMicrophonePermissionAlert];
}
}];
}

@ -11,7 +11,7 @@
#import "UIFont+OWS.h"
#import "UIUtil.h"
#import "UIView+OWS.h"
#import "UIViewController+CameraPermissions.h"
#import "UIViewController+Permissions.h"
#import <SignalServiceKit/OWSError.h>
#import <SignalServiceKit/OWSFingerprint.h>
#import <SignalServiceKit/OWSFingerprintBuilder.h>
@ -118,19 +118,19 @@ NS_ASSUME_NONNULL_BEGIN
{
[super viewDidAppear:animated];
[self ows_askForCameraPermissions:^{
[self ows_askForCameraPermissions:^(BOOL granted) {
if (granted) {
// Camera stops capturing when "sharing" while in capture mode.
// Also, it's less obvious whats being "shared" at this point,
// so just disable sharing when in capture mode.
// Camera stops capturing when "sharing" while in capture mode.
// Also, it's less obvious whats being "shared" at this point,
// so just disable sharing when in capture mode.
DDLogInfo(@"%@ Showing Scanner", self.logTag);
DDLogInfo(@"%@ Showing Scanner", self.logTag);
[self.qrScanningController startCapture];
}
failureCallback:^{
[self.qrScanningController startCapture];
} else {
[self.navigationController popViewControllerAnimated:YES];
}];
}
}];
}
#pragma mark - OWSQRScannerDelegate

@ -6,7 +6,7 @@
#import "OWSDeviceTableViewCell.h"
#import "OWSLinkDeviceViewController.h"
#import "Signal-Swift.h"
#import "UIViewController+CameraPermissions.h"
#import "UIViewController+Permissions.h"
#import <SignalServiceKit/NSTimer+OWS.h>
#import <SignalServiceKit/OWSDevice.h>
#import <SignalServiceKit/OWSDevicesService.h>
@ -256,7 +256,10 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
if (indexPath.section == OWSLinkedDevicesTableViewControllerSectionAddDevice)
{
[self ows_askForCameraPermissions:^{
[self ows_askForCameraPermissions:^(BOOL granted) {
if (!granted) {
return;
}
[self performSegueWithIdentifier:@"LinkDeviceSegue" sender:self];
}];
}

@ -154,7 +154,7 @@ protocol CallServiceObserver: class {
Logger.debug("\(self.TAG) .call setter: \(oldValue?.identifiersForLogs as Optional) -> \(call?.identifiersForLogs as Optional)")
for observer in observers {
observer.value?.didUpdateCall(call:call)
observer.value?.didUpdateCall(call: call)
}
}
}
@ -227,13 +227,13 @@ protocol CallServiceObserver: class {
self.createCallUIAdapter()
NotificationCenter.default.addObserver(self,
selector:#selector(didEnterBackground),
name:NSNotification.Name.UIApplicationDidEnterBackground,
object:nil)
selector: #selector(didEnterBackground),
name: NSNotification.Name.UIApplicationDidEnterBackground,
object: nil)
NotificationCenter.default.addObserver(self,
selector:#selector(didBecomeActive),
name:NSNotification.Name.UIApplicationDidBecomeActive,
object:nil)
selector: #selector(didBecomeActive),
name: NSNotification.Name.UIApplicationDidBecomeActive,
object: nil)
}
deinit {
@ -275,8 +275,8 @@ protocol CallServiceObserver: class {
let errorDescription = "\(TAG) call was unexpectedly already set."
Logger.error(errorDescription)
call.state = .localFailure
OWSProdError(OWSAnalyticsEvents.callServiceCallAlreadySet(), file:#file, function:#function, line:#line)
return Promise(error: CallError.assertionError(description:errorDescription))
OWSProdError(OWSAnalyticsEvents.callServiceCallAlreadySet(), file: #file, function: #function, line: #line)
return Promise(error: CallError.assertionError(description: errorDescription))
}
self.call = call
@ -292,14 +292,14 @@ protocol CallServiceObserver: class {
Logger.debug("\(self.TAG) got ice servers:\(iceServers) for call: \(call.identifiersForLogs)")
guard self.call == call else {
throw CallError.obsoleteCall(description:"obsolete call in \(#function)")
throw CallError.obsoleteCall(description: "obsolete call in \(#function)")
}
guard self.peerConnectionClient == nil else {
let errorDescription = "\(self.TAG) peerconnection was unexpectedly already set."
Logger.error(errorDescription)
OWSProdError(OWSAnalyticsEvents.callServicePeerConnectionAlreadySet(), file:#file, function:#function, line:#line)
throw CallError.assertionError(description:errorDescription)
OWSProdError(OWSAnalyticsEvents.callServicePeerConnectionAlreadySet(), file: #file, function: #function, line: #line)
throw CallError.assertionError(description: errorDescription)
}
let useTurnOnly = Environment.getCurrent().preferences.doCallsHideIPAddress()
@ -312,11 +312,11 @@ protocol CallServiceObserver: class {
return peerConnectionClient.createOffer()
}.then { (sessionDescription: HardenedRTCSessionDescription) -> Promise<Void> in
guard self.call == call else {
throw CallError.obsoleteCall(description:"obsolete call in \(#function)")
throw CallError.obsoleteCall(description: "obsolete call in \(#function)")
}
guard let peerConnectionClient = self.peerConnectionClient else {
owsFail("Missing peerConnectionClient in \(#function)")
throw CallError.obsoleteCall(description:"Missing peerConnectionClient in \(#function)")
throw CallError.obsoleteCall(description: "Missing peerConnectionClient in \(#function)")
}
return peerConnectionClient.setLocalSessionDescription(sessionDescription).then {
@ -326,7 +326,7 @@ protocol CallServiceObserver: class {
}
}.then {
guard self.call == call else {
throw CallError.obsoleteCall(description:"obsolete call in \(#function)")
throw CallError.obsoleteCall(description: "obsolete call in \(#function)")
}
// For outgoing calls, wait until call offer is sent before we send any ICE updates, to ensure message ordering for
@ -340,7 +340,7 @@ protocol CallServiceObserver: class {
// Don't let the outgoing call ring forever. We don't support inbound ringing forever anyway.
let timeout: Promise<Void> = after(interval: connectingTimeoutSeconds).then { () -> Void in
// rejecting a promise by throwing is safely a no-op if the promise has already been fulfilled
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorTimeoutWhileConnectingOutgoing(), file:#file, function:#function, line:#line)
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorTimeoutWhileConnectingOutgoing(), file: #file, function: #function, line: #line)
throw CallError.timeout(description: "timed out waiting to receive call answer")
}
@ -353,10 +353,10 @@ protocol CallServiceObserver: class {
Logger.error("\(self.TAG) placing call \(call.identifiersForLogs) failed with error: \(error)")
if let callError = error as? CallError {
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorOutgoingConnectionFailedInternal(), file:#file, function:#function, line:#line)
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorOutgoingConnectionFailedInternal(), file: #file, function: #function, line: #line)
self.handleFailedCall(failedCall: call, error: callError)
} else {
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorOutgoingConnectionFailedExternal(), file:#file, function:#function, line:#line)
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorOutgoingConnectionFailedExternal(), file: #file, function: #function, line: #line)
let externalError = CallError.externalError(underlyingError: error)
self.handleFailedCall(failedCall: call, error: externalError)
}
@ -378,8 +378,8 @@ protocol CallServiceObserver: class {
}
guard let fulfillReadyToSendIceUpdatesPromise = self.fulfillReadyToSendIceUpdatesPromise else {
OWSProdError(OWSAnalyticsEvents.callServiceMissingFulfillReadyToSendIceUpdatesPromise(), file:#file, function:#function, line:#line)
self.handleFailedCall(failedCall: call, error: CallError.assertionError(description:"failed to create fulfillReadyToSendIceUpdatesPromise"))
OWSProdError(OWSAnalyticsEvents.callServiceMissingFulfillReadyToSendIceUpdatesPromise(), file: #file, function: #function, line: #line)
self.handleFailedCall(failedCall: call, error: CallError.assertionError(description: "failed to create fulfillReadyToSendIceUpdatesPromise"))
return
}
@ -417,8 +417,8 @@ protocol CallServiceObserver: class {
}
guard let peerConnectionClient = self.peerConnectionClient else {
OWSProdError(OWSAnalyticsEvents.callServicePeerConnectionMissing(), file:#file, function:#function, line:#line)
handleFailedCall(failedCall: call, error: CallError.assertionError(description:"peerConnectionClient was unexpectedly nil in \(#function)"))
OWSProdError(OWSAnalyticsEvents.callServicePeerConnectionMissing(), file: #file, function: #function, line: #line)
handleFailedCall(failedCall: call, error: CallError.assertionError(description: "peerConnectionClient was unexpectedly nil in \(#function)"))
return
}
@ -427,10 +427,10 @@ protocol CallServiceObserver: class {
Logger.debug("\(self.TAG) successfully set remote description")
}.catch { error in
if let callError = error as? CallError {
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorHandleReceivedErrorInternal(), file:#file, function:#function, line:#line)
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorHandleReceivedErrorInternal(), file: #file, function: #function, line: #line)
self.handleFailedCall(failedCall: call, error: callError)
} else {
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorHandleReceivedErrorExternal(), file:#file, function:#function, line:#line)
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorHandleReceivedErrorExternal(), file: #file, function: #function, line: #line)
let externalError = CallError.externalError(underlyingError: error)
self.handleFailedCall(failedCall: call, error: externalError)
}
@ -567,7 +567,7 @@ protocol CallServiceObserver: class {
case .idle, .dialing, .remoteRinging:
// If both users are trying to call each other at the same time,
// both should see busy.
handleRemoteBusy(thread:existingCall.thread, callId:existingCall.signalingId)
handleRemoteBusy(thread: existingCall.thread, callId: existingCall.signalingId)
case .answering, .localRinging, .connected, .localFailure, .localHangup, .remoteHangup, .remoteBusy:
// If one user calls another while the other has a "vestigial" call with
// that same user, fail the old call.
@ -644,7 +644,7 @@ protocol CallServiceObserver: class {
let timeout: Promise<Void> = after(interval: connectingTimeoutSeconds).then { () -> Void in
// rejecting a promise by throwing is safely a no-op if the promise has already been fulfilled
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorTimeoutWhileConnectingIncoming(), file:#file, function:#function, line:#line)
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorTimeoutWhileConnectingIncoming(), file: #file, function: #function, line: #line)
throw CallError.timeout(description: "timed out waiting for call to connect")
}
@ -663,10 +663,10 @@ protocol CallServiceObserver: class {
return
}
if let callError = error as? CallError {
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorIncomingConnectionFailedInternal(), file:#file, function:#function, line:#line)
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorIncomingConnectionFailedInternal(), file: #file, function: #function, line: #line)
self.handleFailedCall(failedCall: newCall, error: callError)
} else {
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorIncomingConnectionFailedExternal(), file:#file, function:#function, line:#line)
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorIncomingConnectionFailedExternal(), file: #file, function: #function, line: #line)
let externalError = CallError.externalError(underlyingError: error)
self.handleFailedCall(failedCall: newCall, error: externalError)
}
@ -706,7 +706,7 @@ protocol CallServiceObserver: class {
peerConnectionClient.addRemoteIceCandidate(RTCIceCandidate(sdp: sdp, sdpMLineIndex: lineIndex, sdpMid: mid))
}.catch { error in
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorHandleRemoteAddedIceCandidate(), file:#file, function:#function, line:#line)
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorHandleRemoteAddedIceCandidate(), file: #file, function: #function, line: #line)
Logger.error("\(self.TAG) in \(#function) waitForPeerConnectionClient failed with error: \(error)")
}.retainUntilComplete()
}
@ -719,8 +719,8 @@ protocol CallServiceObserver: class {
AssertIsOnMainThread()
guard let call = self.call else {
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file:#file, function:#function, line:#line)
self.handleFailedCurrentCall(error: CallError.assertionError(description:"ignoring local ice candidate, since there is no current call."))
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line)
self.handleFailedCurrentCall(error: CallError.assertionError(description: "ignoring local ice candidate, since there is no current call."))
return
}
@ -735,8 +735,8 @@ protocol CallServiceObserver: class {
guard call.state != .idle else {
// This will only be called for the current peerConnectionClient, so
// fail the current call.
OWSProdError(OWSAnalyticsEvents.callServiceCallUnexpectedlyIdle(), file:#file, function:#function, line:#line)
self.handleFailedCurrentCall(error: CallError.assertionError(description:"ignoring local ice candidate, since call is now idle."))
OWSProdError(OWSAnalyticsEvents.callServiceCallUnexpectedlyIdle(), file: #file, function: #function, line: #line)
self.handleFailedCurrentCall(error: CallError.assertionError(description: "ignoring local ice candidate, since call is now idle."))
return
}
@ -756,7 +756,7 @@ protocol CallServiceObserver: class {
return
}
}.catch { error in
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorHandleLocalAddedIceCandidate(), file:#file, function:#function, line:#line)
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorHandleLocalAddedIceCandidate(), file: #file, function: #function, line: #line)
Logger.error("\(self.TAG) in \(#function) waitUntilReadyToSendIceUpdates failed with error: \(error)")
}.retainUntilComplete()
}
@ -773,8 +773,8 @@ protocol CallServiceObserver: class {
guard let call = self.call else {
// This will only be called for the current peerConnectionClient, so
// fail the current call.
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file:#file, function:#function, line:#line)
handleFailedCurrentCall(error: CallError.assertionError(description:"\(TAG) ignoring \(#function) since there is no current call."))
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line)
handleFailedCurrentCall(error: CallError.assertionError(description: "\(TAG) ignoring \(#function) since there is no current call."))
return
}
@ -848,16 +848,16 @@ protocol CallServiceObserver: class {
guard let call = self.call else {
// This should never happen; return to a known good state.
owsFail("\(TAG) call was unexpectedly nil in \(#function)")
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file:#file, function:#function, line:#line)
handleFailedCurrentCall(error: CallError.assertionError(description:"\(TAG) call was unexpectedly nil in \(#function)"))
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line)
handleFailedCurrentCall(error: CallError.assertionError(description: "\(TAG) call was unexpectedly nil in \(#function)"))
return
}
guard call.localId == localId else {
// This should never happen; return to a known good state.
owsFail("\(TAG) callLocalId:\(localId) doesn't match current calls: \(call.localId)")
OWSProdError(OWSAnalyticsEvents.callServiceCallIdMismatch(), file:#file, function:#function, line:#line)
handleFailedCurrentCall(error: CallError.assertionError(description:"\(TAG) callLocalId:\(localId) doesn't match current calls: \(call.localId)"))
OWSProdError(OWSAnalyticsEvents.callServiceCallIdMismatch(), file: #file, function: #function, line: #line)
handleFailedCurrentCall(error: CallError.assertionError(description: "\(TAG) callLocalId:\(localId) doesn't match current calls: \(call.localId)"))
return
}
@ -873,8 +873,8 @@ protocol CallServiceObserver: class {
Logger.debug("\(TAG) in \(#function)")
guard let currentCall = self.call else {
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file:#file, function:#function, line:#line)
handleFailedCall(failedCall: call, error: CallError.assertionError(description:"\(TAG) ignoring \(#function) since there is no current call"))
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line)
handleFailedCall(failedCall: call, error: CallError.assertionError(description: "\(TAG) ignoring \(#function) since there is no current call"))
return
}
@ -886,8 +886,8 @@ protocol CallServiceObserver: class {
}
guard let peerConnectionClient = self.peerConnectionClient else {
OWSProdError(OWSAnalyticsEvents.callServicePeerConnectionMissing(), file:#file, function:#function, line:#line)
handleFailedCall(failedCall: call, error: CallError.assertionError(description:"\(TAG) missing peerconnection client in \(#function)"))
OWSProdError(OWSAnalyticsEvents.callServicePeerConnectionMissing(), file: #file, function: #function, line: #line)
handleFailedCall(failedCall: call, error: CallError.assertionError(description: "\(TAG) missing peerconnection client in \(#function)"))
return
}
@ -898,7 +898,7 @@ protocol CallServiceObserver: class {
call.callRecord = callRecord
let message = DataChannelMessage.forConnected(callId: call.signalingId)
peerConnectionClient.sendDataChannelMessage(data: message.asData(), description:"connected", isCritical:true)
peerConnectionClient.sendDataChannelMessage(data: message.asData(), description: "connected", isCritical: true)
handleConnectedCall(call)
}
@ -912,8 +912,8 @@ protocol CallServiceObserver: class {
AssertIsOnMainThread()
guard let peerConnectionClient = self.peerConnectionClient else {
OWSProdError(OWSAnalyticsEvents.callServicePeerConnectionMissing(), file:#file, function:#function, line:#line)
handleFailedCall(failedCall: call, error: CallError.assertionError(description:"\(TAG) peerConnectionClient unexpectedly nil in \(#function)"))
OWSProdError(OWSAnalyticsEvents.callServicePeerConnectionMissing(), file: #file, function: #function, line: #line)
handleFailedCall(failedCall: call, error: CallError.assertionError(description: "\(TAG) peerConnectionClient unexpectedly nil in \(#function)"))
return
}
@ -943,16 +943,16 @@ protocol CallServiceObserver: class {
guard let call = self.call else {
// This should never happen; return to a known good state.
owsFail("\(TAG) call was unexpectedly nil in \(#function)")
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file:#file, function:#function, line:#line)
handleFailedCurrentCall(error: CallError.assertionError(description:"\(TAG) call was unexpectedly nil in \(#function)"))
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line)
handleFailedCurrentCall(error: CallError.assertionError(description: "\(TAG) call was unexpectedly nil in \(#function)"))
return
}
guard call.localId == localId else {
// This should never happen; return to a known good state.
owsFail("\(TAG) callLocalId:\(localId) doesn't match current calls: \(call.localId)")
OWSProdError(OWSAnalyticsEvents.callServiceCallIdMismatch(), file:#file, function:#function, line:#line)
handleFailedCurrentCall(error: CallError.assertionError(description:"\(TAG) callLocalId:\(localId) doesn't match current calls: \(call.localId)"))
OWSProdError(OWSAnalyticsEvents.callServiceCallIdMismatch(), file: #file, function: #function, line: #line)
handleFailedCurrentCall(error: CallError.assertionError(description: "\(TAG) callLocalId:\(localId) doesn't match current calls: \(call.localId)"))
return
}
@ -991,14 +991,14 @@ protocol CallServiceObserver: class {
AssertIsOnMainThread()
guard let currentCall = self.call else {
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file:#file, function:#function, line:#line)
handleFailedCall(failedCall: call, error: CallError.assertionError(description:"\(TAG) ignoring \(#function) since there is no current call"))
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line)
handleFailedCall(failedCall: call, error: CallError.assertionError(description: "\(TAG) ignoring \(#function) since there is no current call"))
return
}
guard call == currentCall else {
OWSProdError(OWSAnalyticsEvents.callServiceCallMismatch(), file:#file, function:#function, line:#line)
handleFailedCall(failedCall: call, error: CallError.assertionError(description:"\(TAG) ignoring \(#function) for call other than current call"))
OWSProdError(OWSAnalyticsEvents.callServiceCallMismatch(), file: #file, function: #function, line: #line)
handleFailedCall(failedCall: call, error: CallError.assertionError(description: "\(TAG) ignoring \(#function) for call other than current call"))
return
}
@ -1013,7 +1013,7 @@ protocol CallServiceObserver: class {
if let peerConnectionClient = self.peerConnectionClient {
// If the call is connected, we can send the hangup via the data channel for faster hangup.
let message = DataChannelMessage.forHangup(callId: call.signalingId)
peerConnectionClient.sendDataChannelMessage(data: message.asData(), description:"hangup", isCritical:true)
peerConnectionClient.sendDataChannelMessage(data: message.asData(), description: "hangup", isCritical: true)
} else {
Logger.info("\(TAG) ending call before peer connection created. Device offline or quick hangup.")
}
@ -1024,7 +1024,7 @@ protocol CallServiceObserver: class {
let sendPromise = self.messageSender.sendPromise(message: callMessage).then {
Logger.debug("\(self.TAG) successfully sent hangup call message to \(call.thread.contactIdentifier())")
}.catch { error in
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorHandleLocalHungupCall(), file:#file, function:#function, line:#line)
OWSProdInfo(OWSAnalyticsEvents.callServiceErrorHandleLocalHungupCall(), file: #file, function: #function, line: #line)
Logger.error("\(self.TAG) failed to send hangup call message to \(call.thread.contactIdentifier()) with error: \(error)")
}
sendPromise.retainUntilComplete()
@ -1103,38 +1103,39 @@ protocol CallServiceObserver: class {
func setHasLocalVideo(hasLocalVideo: Bool) {
AssertIsOnMainThread()
let authStatus = AVCaptureDevice.authorizationStatus(forMediaType:AVMediaTypeVideo)
switch authStatus {
case .notDetermined:
Logger.debug("\(TAG) authStatus: AVAuthorizationStatusNotDetermined")
break
case .restricted:
Logger.debug("\(TAG) authStatus: AVAuthorizationStatusRestricted")
break
case .denied:
Logger.debug("\(TAG) authStatus: AVAuthorizationStatusDenied")
break
case .authorized:
Logger.debug("\(TAG) authStatus: AVAuthorizationStatusAuthorized")
break
}
// We don't need to worry about the user granting or remoting this permission
// during a call while the app is in the background, because changing this
// permission kills the app.
if authStatus != .authorized {
OWSAlerts.showAlert(withTitle:NSLocalizedString("MISSING_CAMERA_PERMISSION_TITLE", comment: "Alert title when camera is not authorized"),
message:NSLocalizedString("MISSING_CAMERA_PERMISSION_MESSAGE", comment: "Alert body when camera is not authorized"))
guard let frontmostViewController = UIApplication.shared.frontmostViewController else {
owsFail("\(TAG) could not identify frontmostViewController in \(#function)")
return
}
frontmostViewController.ows_ask(forCameraPermissions: { [weak self] granted in
guard let strongSelf = self else {
return
}
if (granted) {
// Success callback; camera permissions are granted.
strongSelf.setHasLocalVideoWithCameraPermissions(hasLocalVideo: hasLocalVideo)
} else {
// Failed callback; camera permissions are _NOT_ granted.
// We don't need to worry about the user granting or remoting this permission
// during a call while the app is in the background, because changing this
// permission kills the app.
OWSAlerts.showAlert(withTitle: NSLocalizedString("MISSING_CAMERA_PERMISSION_TITLE", comment: "Alert title when camera is not authorized"),
message: NSLocalizedString("MISSING_CAMERA_PERMISSION_MESSAGE", comment: "Alert body when camera is not authorized"))
}
})
}
private func setHasLocalVideoWithCameraPermissions(hasLocalVideo: Bool) {
AssertIsOnMainThread()
guard let call = self.call else {
// This should never happen; return to a known good state.
owsFail("\(TAG) call was unexpectedly nil in \(#function)")
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file:#file, function:#function, line:#line)
handleFailedCurrentCall(error: CallError.assertionError(description:"\(TAG) call unexpectedly nil in \(#function)"))
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line)
handleFailedCurrentCall(error: CallError.assertionError(description: "\(TAG) call unexpectedly nil in \(#function)"))
return
}
@ -1153,7 +1154,7 @@ protocol CallServiceObserver: class {
func handleCallKitStartVideo() {
AssertIsOnMainThread()
self.setHasLocalVideo(hasLocalVideo:true)
self.setHasLocalVideo(hasLocalVideo: true)
}
/**
@ -1172,8 +1173,8 @@ protocol CallServiceObserver: class {
guard let call = self.call else {
// This should never happen; return to a known good state.
owsFail("\(TAG) received data message, but there is no current call. Ignoring.")
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file:#file, function:#function, line:#line)
handleFailedCurrentCall(error: CallError.assertionError(description:"\(TAG) received data message, but there is no current call. Ignoring."))
OWSProdError(OWSAnalyticsEvents.callServiceCallMissing(), file: #file, function: #function, line: #line)
handleFailedCurrentCall(error: CallError.assertionError(description: "\(TAG) received data message, but there is no current call. Ignoring."))
return
}
@ -1185,8 +1186,8 @@ protocol CallServiceObserver: class {
guard connected.id == call.signalingId else {
// This should never happen; return to a known good state.
owsFail("\(TAG) received connected message for call with id:\(connected.id) but current call has id:\(call.signalingId)")
OWSProdError(OWSAnalyticsEvents.callServiceCallIdMismatch(), file:#file, function:#function, line:#line)
handleFailedCurrentCall(error: CallError.assertionError(description:"\(TAG) received connected message for call with id:\(connected.id) but current call has id:\(call.signalingId)"))
OWSProdError(OWSAnalyticsEvents.callServiceCallIdMismatch(), file: #file, function: #function, line: #line)
handleFailedCurrentCall(error: CallError.assertionError(description: "\(TAG) received connected message for call with id:\(connected.id) but current call has id:\(call.signalingId)"))
return
}
@ -1201,8 +1202,8 @@ protocol CallServiceObserver: class {
guard hangup.id == call.signalingId else {
// This should never happen; return to a known good state.
owsFail("\(TAG) received hangup message for call with id:\(hangup.id) but current call has id:\(call.signalingId)")
OWSProdError(OWSAnalyticsEvents.callServiceCallIdMismatch(), file:#file, function:#function, line:#line)
handleFailedCurrentCall(error: CallError.assertionError(description:"\(TAG) received hangup message for call with id:\(hangup.id) but current call has id:\(call.signalingId)"))
OWSProdError(OWSAnalyticsEvents.callServiceCallIdMismatch(), file: #file, function: #function, line: #line)
handleFailedCurrentCall(error: CallError.assertionError(description: "\(TAG) received hangup message for call with id:\(hangup.id) but current call has id:\(call.signalingId)"))
return
}
@ -1309,8 +1310,8 @@ protocol CallServiceObserver: class {
}
guard let readyToSendIceUpdatesPromise = self.readyToSendIceUpdatesPromise else {
OWSProdError(OWSAnalyticsEvents.callServiceCouldNotCreateReadyToSendIceUpdatesPromise(), file:#file, function:#function, line:#line)
return Promise(error: CallError.assertionError(description:"failed to create readyToSendIceUpdatesPromise"))
OWSProdError(OWSAnalyticsEvents.callServiceCouldNotCreateReadyToSendIceUpdatesPromise(), file: #file, function: #function, line: #line)
return Promise(error: CallError.assertionError(description: "failed to create readyToSendIceUpdatesPromise"))
}
return readyToSendIceUpdatesPromise
@ -1353,8 +1354,8 @@ protocol CallServiceObserver: class {
}
guard let peerConnectionClientPromise = self.peerConnectionClientPromise else {
OWSProdError(OWSAnalyticsEvents.callServiceCouldNotCreatePeerConnectionClientPromise(), file:#file, function:#function, line:#line)
return Promise(error: CallError.assertionError(description:"failed to create peerConnectionClientPromise"))
OWSProdError(OWSAnalyticsEvents.callServiceCouldNotCreatePeerConnectionClientPromise(), file: #file, function: #function, line: #line)
return Promise(error: CallError.assertionError(description: "failed to create peerConnectionClientPromise"))
}
return peerConnectionClientPromise
@ -1576,8 +1577,8 @@ protocol CallServiceObserver: class {
self.peerConnectionClient?.setLocalVideoEnabled(enabled: shouldHaveLocalVideoTrack)
let message = DataChannelMessage.forVideoStreamingStatus(callId: call.signalingId, enabled:shouldHaveLocalVideoTrack)
peerConnectionClient.sendDataChannelMessage(data: message.asData(), description:"videoStreamingStatus")
let message = DataChannelMessage.forVideoStreamingStatus(callId: call.signalingId, enabled: shouldHaveLocalVideoTrack)
peerConnectionClient.sendDataChannelMessage(data: message.asData(), description: "videoStreamingStatus")
}
// MARK: - Observers
@ -1592,9 +1593,9 @@ protocol CallServiceObserver: class {
let call = self.call
let localVideoTrack = self.localVideoTrack
let remoteVideoTrack = self.isRemoteVideoEnabled ? self.remoteVideoTrack : nil
observer.didUpdateVideoTracks(call:call,
localVideoTrack:localVideoTrack,
remoteVideoTrack:remoteVideoTrack)
observer.didUpdateVideoTracks(call: call,
localVideoTrack: localVideoTrack,
remoteVideoTrack: remoteVideoTrack)
}
// The observer-related methods should be invoked on the main thread.
@ -1621,9 +1622,9 @@ protocol CallServiceObserver: class {
let remoteVideoTrack = self.isRemoteVideoEnabled ? self.remoteVideoTrack : nil
for observer in observers {
observer.value?.didUpdateVideoTracks(call:call,
localVideoTrack:localVideoTrack,
remoteVideoTrack:remoteVideoTrack)
observer.value?.didUpdateVideoTracks(call: call,
localVideoTrack: localVideoTrack,
remoteVideoTrack: remoteVideoTrack)
}
}
@ -1669,7 +1670,7 @@ protocol CallServiceObserver: class {
let frontmostViewController = UIApplication.shared.frontmostViewControllerIgnoringAlerts
guard nil != frontmostViewController as? CallViewController else {
OWSProdError(OWSAnalyticsEvents.callServiceCallViewCouldNotPresent(), file:#file, function:#function, line:#line)
OWSProdError(OWSAnalyticsEvents.callServiceCallViewCouldNotPresent(), file: #file, function: #function, line: #line)
owsFail("\(TAG) in \(#function) Call terminated due to call view presentation delay: \(frontmostViewController.debugDescription).")
self.terminateCall()
return

@ -44,6 +44,10 @@ import Foundation
owsFail("\(TAG) can't initiate call because callUIAdapter is nil")
return false
}
guard let frontmostViewController = UIApplication.shared.frontmostViewController else {
owsFail("\(TAG) could not identify frontmostViewController in \(#function)")
return false
}
let showedAlert = SafetyNumberConfirmationAlert.presentAlertIfNecessary(recipientId: recipientId,
confirmationText: CallStrings.confirmAndCallButtonTitle,
@ -59,17 +63,22 @@ import Foundation
// Check for microphone permissions
// Alternative way without prompting for permissions:
// if AVAudioSession.sharedInstance().recordPermission() == .denied {
AVAudioSession.sharedInstance().requestRecordPermission { isGranted in
DispatchQueue.main.async {
// Here the permissions are either granted or denied
guard isGranted == true else {
Logger.warn("\(self.TAG) aborting due to missing microphone permissions.")
OWSAlerts.showNoMicrophonePermissionAlert()
return
}
callUIAdapter.startAndShowOutgoingCall(recipientId: recipientId)
frontmostViewController.ows_ask(forMicrophonePermissions: { [weak self] granted in
// Success callback; camera permissions are granted.
guard let strongSelf = self else {
return
}
}
// Here the permissions are either granted or denied
guard granted == true else {
Logger.warn("\(strongSelf.TAG) aborting due to missing microphone permissions.")
OWSAlerts.showNoMicrophonePermissionAlert()
return
}
callUIAdapter.startAndShowOutgoingCall(recipientId: recipientId)
})
return true
}
}

@ -1,76 +0,0 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "UIViewController+CameraPermissions.h"
#import "Signal-Swift.h"
#import "UIUtil.h"
#import <AVFoundation/AVFoundation.h>
NS_ASSUME_NONNULL_BEGIN
@implementation UIViewController (CameraPermissions)
- (void)ows_askForCameraPermissions:(void (^)(void))permissionsGrantedCallback
{
[self ows_askForCameraPermissions:permissionsGrantedCallback failureCallback:nil];
}
- (void)ows_askForCameraPermissions:(void (^)(void))permissionsGrantedCallback
failureCallback:(nullable void (^)(void))failureCallback
{
// Avoid nil tests below.
if (!failureCallback) {
failureCallback = ^{
};
}
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
DDLogError(@"Camera ImagePicker source not available");
failureCallback();
return;
}
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (status == AVAuthorizationStatusDenied) {
UIAlertController* alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"MISSING_CAMERA_PERMISSION_TITLE", @"Alert title")
message:NSLocalizedString(@"MISSING_CAMERA_PERMISSION_MESSAGE", @"Alert body")
preferredStyle:UIAlertControllerStyleAlert];
NSString *settingsTitle = NSLocalizedString(@"OPEN_SETTINGS_BUTTON", @"Button text which opens the settings app");
UIAlertAction *openSettingsAction = [UIAlertAction actionWithTitle:settingsTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[[UIApplication sharedApplication] openSystemSettings];
failureCallback();
}];
[alert addAction:openSettingsAction];
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:CommonStrings.dismissButton
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
failureCallback();
}];
[alert addAction:dismissAction];
[self presentViewController:alert animated:YES completion:nil];
} else if (status == AVAuthorizationStatusAuthorized) {
permissionsGrantedCallback();
} else if (status == AVAuthorizationStatusNotDetermined) {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo
completionHandler:^(BOOL granted) {
dispatch_async(dispatch_get_main_queue(), ^{
if (granted) {
permissionsGrantedCallback();
} else {
failureCallback();
}
});
}];
} else {
DDLogError(@"Unknown AVAuthorizationStatus: %ld", (long)status);
failureCallback();
}
}
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,17 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIViewController (Permissions)
- (void)ows_askForCameraPermissions:(void (^)(BOOL granted))callback;
- (void)ows_askForMicrophonePermissions:(void (^)(BOOL granted))callback;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,97 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "Signal-Swift.h"
#import "UIUtil.h"
#import "UIViewController+Permissions.h"
#import <AVFoundation/AVFoundation.h>
#import <SignalServiceKit/Threading.h>
NS_ASSUME_NONNULL_BEGIN
@implementation UIViewController (Permissions)
- (void)ows_askForCameraPermissions:(void (^)(BOOL granted))callbackParam
{
DDLogVerbose(@"[%@] ows_askForCameraPermissions", NSStringFromClass(self.class));
// Ensure callback is invoked on main thread.
void (^callback)(BOOL) = ^(BOOL granted) {
DispatchMainThreadSafe(^{
callbackParam(granted);
});
};
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
DDLogError(@"Skipping camera permissions request when app is in background.");
callback(NO);
return;
}
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
DDLogError(@"Camera ImagePicker source not available");
callback(NO);
return;
}
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (status == AVAuthorizationStatusDenied) {
UIAlertController *alert = [UIAlertController
alertControllerWithTitle:NSLocalizedString(@"MISSING_CAMERA_PERMISSION_TITLE", @"Alert title")
message:NSLocalizedString(@"MISSING_CAMERA_PERMISSION_MESSAGE", @"Alert body")
preferredStyle:UIAlertControllerStyleAlert];
NSString *settingsTitle
= NSLocalizedString(@"OPEN_SETTINGS_BUTTON", @"Button text which opens the settings app");
UIAlertAction *openSettingsAction =
[UIAlertAction actionWithTitle:settingsTitle
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
[[UIApplication sharedApplication] openSystemSettings];
callback(NO);
}];
[alert addAction:openSettingsAction];
UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:CommonStrings.dismissButton
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
callback(NO);
}];
[alert addAction:dismissAction];
[self presentViewController:alert animated:YES completion:nil];
} else if (status == AVAuthorizationStatusAuthorized) {
callback(YES);
} else if (status == AVAuthorizationStatusNotDetermined) {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo
completionHandler:callback];
} else {
DDLogError(@"Unknown AVAuthorizationStatus: %ld", (long)status);
callback(NO);
}
}
- (void)ows_askForMicrophonePermissions:(void (^)(BOOL granted))callbackParam
{
DDLogVerbose(@"[%@] ows_askForMicrophonePermissions", NSStringFromClass(self.class));
// Ensure callback is invoked on main thread.
void (^callback)(BOOL) = ^(BOOL granted) {
DispatchMainThreadSafe(^{
callbackParam(granted);
});
};
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
DDLogError(@"Skipping microphone permissions request when app is in background.");
callback(NO);
return;
}
[[AVAudioSession sharedInstance] requestRecordPermission:callback];
}
@end
NS_ASSUME_NONNULL_END
Loading…
Cancel
Save