From 6ce33381a8a6e157b380c9248c1f8eadb2c8c144 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 30 Jan 2017 10:31:43 -0500 Subject: [PATCH 1/6] Prevent screen from dimming or device from locking during video call. // FREEBIE --- Signal/src/call/CallService.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Signal/src/call/CallService.swift b/Signal/src/call/CallService.swift index 59913b207..1991e4691 100644 --- a/Signal/src/call/CallService.swift +++ b/Signal/src/call/CallService.swift @@ -1114,6 +1114,10 @@ protocol CallServiceObserver: class { } } } + + // Prevent screen from dimming during video call. + let hasLocalOrRemoteVideo = localVideoTrack != nil || remoteVideoTrack != nil + UIApplication.shared.isIdleTimerDisabled = hasLocalOrRemoteVideo } } From 50addfa4e883db637a8a34585f45f09c3820f469 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 30 Jan 2017 10:51:53 -0500 Subject: [PATCH 2/6] Remove camera constraints. // FREEBIE --- Signal/src/call/PeerConnectionClient.swift | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Signal/src/call/PeerConnectionClient.swift b/Signal/src/call/PeerConnectionClient.swift index 2db685218..ae1325a40 100644 --- a/Signal/src/call/PeerConnectionClient.swift +++ b/Signal/src/call/PeerConnectionClient.swift @@ -147,15 +147,9 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD return } - // TODO: What are the best values to use here? - let mediaConstraintsDictionary = [ - kMediaConstraintsMinWidth: "240", - kMediaConstraintsMinHeight: "320", - kMediaConstraintsMaxWidth: "240", - kMediaConstraintsMaxHeight: "320" - ] + // TODO: We could cap the maximum video size. let cameraConstraints = RTCMediaConstraints(mandatoryConstraints:nil, - optionalConstraints:mediaConstraintsDictionary) + optionalConstraints:nil) // TODO: Revisit the cameraConstraints. let videoSource = factory.avFoundationVideoSource(with: cameraConstraints) From 2ef80e569d6c88084ec708d7761a0af67b0957b6 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 30 Jan 2017 13:46:19 -0500 Subject: [PATCH 3/6] Improve thread safety in call ui adapter and adatapees. // FREEBIE --- Signal/src/call/CallService.swift | 22 ++++++--- Signal/src/call/NonCallKitCallUIAdaptee.swift | 26 ++++++++++ .../Speakerbox/CallKitCallUIAdaptee.swift | 48 +++++++++++++++++++ .../call/UserInterface/CallUIAdapter.swift | 38 ++++++++++++++- 4 files changed, 127 insertions(+), 7 deletions(-) diff --git a/Signal/src/call/CallService.swift b/Signal/src/call/CallService.swift index 1991e4691..f882f154d 100644 --- a/Signal/src/call/CallService.swift +++ b/Signal/src/call/CallService.swift @@ -309,7 +309,9 @@ protocol CallServiceObserver: class { in: thread) callRecord.save() - self.callUIAdapter.reportMissedCall(call) + DispatchQueue.main.async { + self.callUIAdapter.reportMissedCall(call) + } } /** @@ -514,7 +516,9 @@ protocol CallServiceObserver: class { call.state = .remoteRinging case .answering: call.state = .localRinging - self.callUIAdapter.reportIncomingCall(call, thread: thread) + DispatchQueue.main.async { + self.callUIAdapter.reportIncomingCall(call, thread: thread) + } // cancel connection timeout self.fulfillCallConnectedPromise?() case .remoteRinging: @@ -552,7 +556,9 @@ protocol CallServiceObserver: class { call.state = .remoteHangup // Notify UI - callUIAdapter.remoteDidHangupCall(call) + DispatchQueue.main.async { + self.callUIAdapter.remoteDidHangupCall(call) + } // self.call is nil'd in `terminateCall`, so it's important we update it's state *before* calling `terminateCall` terminateCall() @@ -821,7 +827,9 @@ protocol CallServiceObserver: class { return } - callUIAdapter.recipientAcceptedCall(call) + DispatchQueue.main.async { + self.callUIAdapter.recipientAcceptedCall(call) + } handleConnectedCall(call) } else if message.hasHangup() { @@ -958,7 +966,9 @@ protocol CallServiceObserver: class { // It's essential to set call.state before terminateCall, because terminateCall nils self.call call.error = error call.state = .localFailure - callUIAdapter.failCall(call, error: error) + DispatchQueue.main.async { + self.callUIAdapter.failCall(call, error: error) + } } else { // This can happen when we receive an out of band signaling message (e.g. IceUpdate) // after the call has ended @@ -1114,7 +1124,7 @@ protocol CallServiceObserver: class { } } } - + // Prevent screen from dimming during video call. let hasLocalOrRemoteVideo = localVideoTrack != nil || remoteVideoTrack != nil UIApplication.shared.isIdleTimerDisabled = hasLocalOrRemoteVideo diff --git a/Signal/src/call/NonCallKitCallUIAdaptee.swift b/Signal/src/call/NonCallKitCallUIAdaptee.swift index 876c9d68c..cc025565f 100644 --- a/Signal/src/call/NonCallKitCallUIAdaptee.swift +++ b/Signal/src/call/NonCallKitCallUIAdaptee.swift @@ -23,6 +23,8 @@ class NonCallKitCallUIAdaptee: CallUIAdaptee { } func startOutgoingCall(handle: String) -> SignalCall { + AssertIsOnMainThread() + let call = SignalCall.outgoingCall(localId: UUID(), remotePhoneNumber: handle) CallService.signalingQueue.async { @@ -37,6 +39,8 @@ class NonCallKitCallUIAdaptee: CallUIAdaptee { } func reportIncomingCall(_ call: SignalCall, callerName: String) { + AssertIsOnMainThread() + Logger.debug("\(TAG) \(#function)") // present Call View controller @@ -52,10 +56,14 @@ class NonCallKitCallUIAdaptee: CallUIAdaptee { } func reportMissedCall(_ call: SignalCall, callerName: String) { + AssertIsOnMainThread() + notificationsAdapter.presentMissedCall(call, callerName: callerName) } func answerCall(localId: UUID) { + AssertIsOnMainThread() + CallService.signalingQueue.async { guard let call = self.callService.call else { assertionFailure("\(self.TAG) in \(#function) No current call.") @@ -72,6 +80,8 @@ class NonCallKitCallUIAdaptee: CallUIAdaptee { } func answerCall(_ call: SignalCall) { + AssertIsOnMainThread() + CallService.signalingQueue.async { guard call.localId == self.callService.call?.localId else { assertionFailure("\(self.TAG) in \(#function) localId does not match current call") @@ -84,6 +94,8 @@ class NonCallKitCallUIAdaptee: CallUIAdaptee { } func declineCall(localId: UUID) { + AssertIsOnMainThread() + CallService.signalingQueue.async { guard let call = self.callService.call else { assertionFailure("\(self.TAG) in \(#function) No current call.") @@ -100,6 +112,8 @@ class NonCallKitCallUIAdaptee: CallUIAdaptee { } func declineCall(_ call: SignalCall) { + AssertIsOnMainThread() + CallService.signalingQueue.async { guard call.localId == self.callService.call?.localId else { assertionFailure("\(self.TAG) in \(#function) localId does not match current call") @@ -111,10 +125,14 @@ class NonCallKitCallUIAdaptee: CallUIAdaptee { } func recipientAcceptedCall(_ call: SignalCall) { + AssertIsOnMainThread() + PeerConnectionClient.startAudioSession() } func localHangupCall(_ call: SignalCall) { + AssertIsOnMainThread() + CallService.signalingQueue.async { // If both parties hang up at the same moment, // call might already be nil. @@ -128,14 +146,20 @@ class NonCallKitCallUIAdaptee: CallUIAdaptee { } internal func remoteDidHangupCall(_ call: SignalCall) { + AssertIsOnMainThread() + Logger.debug("\(TAG) in \(#function) is no-op") } internal func failCall(_ call: SignalCall, error: CallError) { + AssertIsOnMainThread() + Logger.debug("\(TAG) in \(#function) is no-op") } func setIsMuted(call: SignalCall, isMuted: Bool) { + AssertIsOnMainThread() + CallService.signalingQueue.async { guard call.localId == self.callService.call?.localId else { assertionFailure("\(self.TAG) in \(#function) localId does not match current call") @@ -147,6 +171,8 @@ class NonCallKitCallUIAdaptee: CallUIAdaptee { } func setHasLocalVideo(call: SignalCall, hasLocalVideo: Bool) { + AssertIsOnMainThread() + CallService.signalingQueue.async { guard call.localId == self.callService.call?.localId else { assertionFailure("\(self.TAG) in \(#function) localId does not match current call") diff --git a/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift b/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift index e4599a29e..f427d6032 100644 --- a/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift +++ b/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift @@ -49,6 +49,8 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { } init(callService: CallService, notificationsAdapter: CallNotificationsAdapter) { + AssertIsOnMainThread() + self.callManager = CallKitCallManager() self.callService = callService self.notificationsAdapter = notificationsAdapter @@ -62,6 +64,8 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { // MARK: CallUIAdaptee func startOutgoingCall(handle: String) -> SignalCall { + AssertIsOnMainThread() + let call = SignalCall.outgoingCall(localId: UUID(), remotePhoneNumber: handle) // Add the new outgoing call to the app's list of calls. @@ -74,11 +78,15 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { // Called from CallService after call has ended to clean up any remaining CallKit call state. func failCall(_ call: SignalCall, error: CallError) { + AssertIsOnMainThread() + provider.reportCall(with: call.localId, endedAt: Date(), reason: CXCallEndedReason.failed) self.callManager.removeCall(call) } func reportIncomingCall(_ call: SignalCall, callerName: String) { + AssertIsOnMainThread() + // Construct a CXCallUpdate describing the incoming call, including the caller. let update = CXCallUpdate() update.remoteHandle = CXHandle(type: .phoneNumber, value: call.remotePhoneNumber) @@ -104,39 +112,57 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { } func answerCall(localId: UUID) { + AssertIsOnMainThread() + assertionFailure("CallKit should answer calls via system call screen, not via notifications.") } func answerCall(_ call: SignalCall) { + AssertIsOnMainThread() + callManager.answer(call: call) } func declineCall(localId: UUID) { + AssertIsOnMainThread() + assertionFailure("CallKit should decline calls via system call screen, not via notifications.") } func declineCall(_ call: SignalCall) { + AssertIsOnMainThread() + callManager.localHangup(call: call) } func recipientAcceptedCall(_ call: SignalCall) { + AssertIsOnMainThread() + // no - op // TODO provider update call connected? } func localHangupCall(_ call: SignalCall) { + AssertIsOnMainThread() + callManager.localHangup(call: call) } func remoteDidHangupCall(_ call: SignalCall) { + AssertIsOnMainThread() + provider.reportCall(with: call.localId, endedAt: nil, reason: CXCallEndedReason.remoteEnded) } func setIsMuted(call: SignalCall, isMuted: Bool) { + AssertIsOnMainThread() + callManager.setIsMuted(call: call, isMuted: isMuted) } func setHasLocalVideo(call: SignalCall, hasLocalVideo: Bool) { + AssertIsOnMainThread() + let update = CXCallUpdate() update.remoteHandle = CXHandle(type: .phoneNumber, value: call.remotePhoneNumber) update.hasVideo = hasLocalVideo @@ -152,6 +178,8 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { // MARK: CXProviderDelegate func providerDidReset(_ provider: CXProvider) { + AssertIsOnMainThread() + Logger.debug("\(TAG) in \(#function)") // TODO @@ -174,6 +202,8 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { } func provider(_ provider: CXProvider, perform action: CXStartCallAction) { + AssertIsOnMainThread() + Logger.debug("\(TAG) in \(#function) CXStartCallAction") // TODO does this work when `action.handle.value` is not in e164 format, e.g. if called via intent? @@ -207,6 +237,8 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { } func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { + AssertIsOnMainThread() + Logger.debug("\(TAG) Received \(#function) CXAnswerCallAction") // Retrieve the SpeakerboxCall instance corresponding to the action's call UUID guard let call = callManager.callWithLocalId(action.callUUID) else { @@ -232,6 +264,8 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { } public func provider(_ provider: CXProvider, perform action: CXEndCallAction) { + AssertIsOnMainThread() + Logger.debug("\(TAG) Received \(#function) CXEndCallAction") guard let call = callManager.callWithLocalId(action.callUUID) else { action.fail() @@ -257,6 +291,8 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { } public func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) { + AssertIsOnMainThread() + Logger.debug("\(TAG) Received \(#function) CXSetHeldCallAction") guard let call = callManager.callWithLocalId(action.callUUID) else { action.fail() @@ -282,6 +318,8 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { } public func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) { + AssertIsOnMainThread() + Logger.debug("\(TAG) Received \(#function) CXSetMutedCallAction") guard callManager.callWithLocalId(action.callUUID) != nil else { Logger.error("\(TAG) Failing CXSetMutedCallAction for unknown call: \(action.callUUID)") @@ -296,20 +334,28 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { } public func provider(_ provider: CXProvider, perform action: CXSetGroupCallAction) { + AssertIsOnMainThread() + Logger.warn("\(TAG) unimplemented \(#function) for CXSetGroupCallAction") } public func provider(_ provider: CXProvider, perform action: CXPlayDTMFCallAction) { + AssertIsOnMainThread() + Logger.warn("\(TAG) unimplemented \(#function) for CXPlayDTMFCallAction") } func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) { + AssertIsOnMainThread() + Logger.debug("\(TAG) Timed out \(#function)") // React to the action timeout if necessary, such as showing an error UI. } func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) { + AssertIsOnMainThread() + Logger.debug("\(TAG) Received \(#function)") // Start recording @@ -317,6 +363,8 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { } func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) { + AssertIsOnMainThread() + Logger.debug("\(TAG) Received \(#function)") /* diff --git a/Signal/src/call/UserInterface/CallUIAdapter.swift b/Signal/src/call/UserInterface/CallUIAdapter.swift index b448dcd82..98e581a8c 100644 --- a/Signal/src/call/UserInterface/CallUIAdapter.swift +++ b/Signal/src/call/UserInterface/CallUIAdapter.swift @@ -63,6 +63,8 @@ extension CallUIAdaptee { private let audioService: CallAudioService required init(callService: CallService, contactsManager: OWSContactsManager, notificationsAdapter: CallNotificationsAdapter) { + AssertIsOnMainThread() + self.contactsManager = contactsManager if Platform.isSimulator { // CallKit doesn't seem entirely supported in simulator. @@ -82,6 +84,8 @@ extension CallUIAdaptee { } internal func reportIncomingCall(_ call: SignalCall, thread: TSContactThread) { + AssertIsOnMainThread() + call.addObserverAndSyncState(observer: audioService) let callerName = self.contactsManager.displayName(forPhoneIdentifier: call.remotePhoneNumber) @@ -89,11 +93,15 @@ extension CallUIAdaptee { } internal func reportMissedCall(_ call: SignalCall) { + AssertIsOnMainThread() + let callerName = self.contactsManager.displayName(forPhoneIdentifier: call.remotePhoneNumber) adaptee.reportMissedCall(call, callerName: callerName) } internal func startOutgoingCall(handle: String) -> SignalCall { + AssertIsOnMainThread() + let call = adaptee.startOutgoingCall(handle: handle) call.addObserverAndSyncState(observer: audioService) @@ -101,56 +109,82 @@ extension CallUIAdaptee { } internal func answerCall(localId: UUID) { + AssertIsOnMainThread() + adaptee.answerCall(localId: localId) } internal func answerCall(_ call: SignalCall) { + AssertIsOnMainThread() + adaptee.answerCall(call) } internal func declineCall(localId: UUID) { + AssertIsOnMainThread() + adaptee.declineCall(localId: localId) } internal func declineCall(_ call: SignalCall) { + AssertIsOnMainThread() + adaptee.declineCall(call) } internal func callBack(recipientId: String) { + AssertIsOnMainThread() + adaptee.callBack(recipientId: recipientId) } internal func recipientAcceptedCall(_ call: SignalCall) { + AssertIsOnMainThread() + adaptee.recipientAcceptedCall(call) } internal func remoteDidHangupCall(_ call: SignalCall) { + AssertIsOnMainThread() + adaptee.remoteDidHangupCall(call) } internal func localHangupCall(_ call: SignalCall) { + AssertIsOnMainThread() + adaptee.localHangupCall(call) } internal func failCall(_ call: SignalCall, error: CallError) { + AssertIsOnMainThread() + adaptee.failCall(call, error: error) } internal func showCall(_ call: SignalCall) { + AssertIsOnMainThread() + adaptee.showCall(call) } internal func setIsMuted(call: SignalCall, isMuted: Bool) { + AssertIsOnMainThread() + // With CallKit, muting is handled by a CXAction, so it must go through the adaptee adaptee.setIsMuted(call: call, isMuted: isMuted) } internal func setHasLocalVideo(call: SignalCall, hasLocalVideo: Bool) { + AssertIsOnMainThread() + adaptee.setHasLocalVideo(call: call, hasLocalVideo: hasLocalVideo) } internal func setIsSpeakerphoneEnabled(call: SignalCall, isEnabled: Bool) { - // Speakerphone is not handled by CallKit (e.g. there is no CXAction), so we handle it w/o going through the + AssertIsOnMainThread() + + // Speakerphone is not handled by CallKit (e.g. there is no CXAction), so we handle it w/o going through the // adaptee, relying on the AudioService CallObserver to put the system in a state consistent with the call's // assigned property. CallService.signalingQueue.async { @@ -160,6 +194,8 @@ extension CallUIAdaptee { // CallKit handles ringing state on it's own. But for non-call kit we trigger ringing start/stop manually. internal var hasManualRinger: Bool { + AssertIsOnMainThread() + return adaptee.hasManualRinger } } From da53368bc27e858b76feb3109aec0e5dab7425b1 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 30 Jan 2017 13:46:58 -0500 Subject: [PATCH 4/6] Show alert when user tries to activate local video without camera permissions. // FREEBIE --- Signal/src/call/CallService.swift | 31 +++++++++++++++++++ .../translations/en.lproj/Localizable.strings | 8 ++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Signal/src/call/CallService.swift b/Signal/src/call/CallService.swift index f882f154d..6974cfc7f 100644 --- a/Signal/src/call/CallService.swift +++ b/Signal/src/call/CallService.swift @@ -779,6 +779,37 @@ protocol CallServiceObserver: class { func setHasLocalVideo(hasLocalVideo: Bool) { assertOnSignalingQueue() + 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 + } + + // TODO: We could also check this whenever the app returns from the background, + // since the user could disable this priviledge at all times. + if authStatus != .authorized { + DispatchQueue.main.async { + let title = NSLocalizedString("CAMERA_PERMISSION_MISSING_TITLE", comment: "Alert title when camera is not authorized") + let message = NSLocalizedString("CAMERA_PERMISSION_MISSING_BODY", comment: "Alert body when camera is not authorized") + let okButton = NSLocalizedString("OK", comment:"") + + let alert = UIAlertView(title:title, message:message, delegate:nil, cancelButtonTitle:okButton) + alert.show() + } + + return + } + guard let peerConnectionClient = self.peerConnectionClient else { handleFailedCall(error: .assertionError(description:"\(TAG) peerConnectionClient unexpectedly nil in \(#function)")) return diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 2970489d1..a242ee01e 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -68,7 +68,7 @@ "ATTACHMENT_QUEUED" = "New attachment queued for retrieval."; /* No comment provided by engineer. */ -"AUDIO_PERMISSION_MESSAGE" = "Signal requires access to your microphone to work properly. You can restore the permission in the Settings app >> Privacy >> Microphone >> Signal"; +"AUDIO_PERMISSION_MESSAGE" = "Signal requires access to your microphone to work properly. You can grant this permission in the Settings app >> Privacy >> Microphone >> Signal"; /* Accessibilty label for placing call button */ "CALL_LABEL" = "Call"; @@ -76,6 +76,12 @@ /* notification action */ "CALLBACK_BUTTON_TITLE" = "Call back"; +/* Alert body when camera is not authorized */ +"CAMERA_PERMISSION_MISSING_BODY" = "Signal requires access to your camera for video calls. You can grant this permission in the Settings app >> Privacy >> Camera >> Signal"; + +/* Alert title when camera is not authorized */ +"CAMERA_PERMISSION_MISSING_TITLE" = "Error"; + /* Activity Sheet label */ "COMPARE_SAFETY_NUMBER_ACTION" = "Compare with Clipboard"; From 9a0a7bb6b146a55ccb872db40b0df38f808700fb Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 30 Jan 2017 13:54:44 -0500 Subject: [PATCH 5/6] Show alert when user tries to activate local video without camera permissions. // FREEBIE --- Signal/src/call/CallService.swift | 5 +++-- Signal/src/view controllers/CallViewController.swift | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Signal/src/call/CallService.swift b/Signal/src/call/CallService.swift index 6974cfc7f..f4a004d28 100644 --- a/Signal/src/call/CallService.swift +++ b/Signal/src/call/CallService.swift @@ -795,8 +795,9 @@ protocol CallServiceObserver: class { break } - // TODO: We could also check this whenever the app returns from the background, - // since the user could disable this priviledge at all times. + // We don't need to worry about the user granting or remotinv this permission + // during a call while the app is in the background, because changing this + // permission kills the app. if authStatus != .authorized { DispatchQueue.main.async { let title = NSLocalizedString("CAMERA_PERMISSION_MISSING_TITLE", comment: "Alert title when camera is not authorized") diff --git a/Signal/src/view controllers/CallViewController.swift b/Signal/src/view controllers/CallViewController.swift index 458722176..e0f46fa0c 100644 --- a/Signal/src/view controllers/CallViewController.swift +++ b/Signal/src/view controllers/CallViewController.swift @@ -662,8 +662,6 @@ class CallViewController: UIViewController, CallObserver, CallServiceObserver, R func didPressVideo(sender: UIButton) { Logger.info("\(TAG) called \(#function)") let hasLocalVideo = !sender.isSelected - audioModeVideoButton.isSelected = hasLocalVideo - videoModeVideoButton.isSelected = hasLocalVideo if let call = self.call { callUIAdapter.setHasLocalVideo(call: call, hasLocalVideo: hasLocalVideo) } else { From c8e5884087a72cfcafddc12b2a81839139f9a102 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 30 Jan 2017 15:54:31 -0500 Subject: [PATCH 6/6] Respond to CR. // FREEBIE --- Signal/src/call/CallService.swift | 18 +++++++++++------- .../translations/en.lproj/Localizable.strings | 14 +++++--------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Signal/src/call/CallService.swift b/Signal/src/call/CallService.swift index f4a004d28..f23d13131 100644 --- a/Signal/src/call/CallService.swift +++ b/Signal/src/call/CallService.swift @@ -795,13 +795,13 @@ protocol CallServiceObserver: class { break } - // We don't need to worry about the user granting or remotinv this permission + // 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 { DispatchQueue.main.async { - let title = NSLocalizedString("CAMERA_PERMISSION_MISSING_TITLE", comment: "Alert title when camera is not authorized") - let message = NSLocalizedString("CAMERA_PERMISSION_MISSING_BODY", comment: "Alert body when camera is not authorized") + let title = NSLocalizedString("MISSING_CAMERA_PERMISSION_TITLE", comment: "Alert title when camera is not authorized") + let message = NSLocalizedString("MISSING_CAMERA_PERMISSION_MESSAGE", comment: "Alert body when camera is not authorized") let okButton = NSLocalizedString("OK", comment:"") let alert = UIAlertView(title:title, message:message, delegate:nil, cancelButtonTitle:okButton) @@ -1155,11 +1155,15 @@ protocol CallServiceObserver: class { remoteVideoTrack:remoteVideoTrack) } } - } - // Prevent screen from dimming during video call. - let hasLocalOrRemoteVideo = localVideoTrack != nil || remoteVideoTrack != nil - UIApplication.shared.isIdleTimerDisabled = hasLocalOrRemoteVideo + // Prevent screen from dimming during video call. + // + // fireDidUpdateVideoTracks() is called by the video track setters, + // which are cleared when the call ends. That ensures that this timer + // will be re-enabled. + let hasLocalOrRemoteVideo = localVideoTrack != nil || remoteVideoTrack != nil + UIApplication.shared.isIdleTimerDisabled = hasLocalOrRemoteVideo + } } } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index a242ee01e..f5bd472fc 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -76,12 +76,6 @@ /* notification action */ "CALLBACK_BUTTON_TITLE" = "Call back"; -/* Alert body when camera is not authorized */ -"CAMERA_PERMISSION_MISSING_BODY" = "Signal requires access to your camera for video calls. You can grant this permission in the Settings app >> Privacy >> Camera >> Signal"; - -/* Alert title when camera is not authorized */ -"CAMERA_PERMISSION_MISSING_TITLE" = "Error"; - /* Activity Sheet label */ "COMPARE_SAFETY_NUMBER_ACTION" = "Compare with Clipboard"; @@ -455,10 +449,12 @@ notification title */ "MISSED_CALL" = "Missed call"; -/* Alert body */ -"MISSING_CAMERA_PERMISSION_MESSAGE" = "You previously declined to let Signal access your camera. Update your system settings to proceed."; +/* Alert body + Alert body when camera is not authorized */ +"MISSING_CAMERA_PERMISSION_MESSAGE" = "Signal needs access to your camera for video calls. You can grant this permission in the Settings app >> Privacy >> Camera >> Signal"; -/* Alert title */ +/* Alert title + Alert title when camera is not authorized */ "MISSING_CAMERA_PERMISSION_TITLE" = "Signal needs to access your camera."; /* notification body