Skip to content

Commit

Permalink
Merge pull request #957 from monal-im/6.0rc1
Browse files Browse the repository at this point in the history
- Implement webrtc renomination
- Fix muc avatar fetching
- Fix bookmarks2 bugs
- Allow outgoing calls even to clients not in our contact list
- Fix audio feedback when establishing calls and after call errors
- Only show contacts in contacts panel if they are in our roster
- Implement invitations using qr codes instead of xmpp: uris
- Implement new image viewer compatible with ios 17
- Implement gif support in image viewer
  • Loading branch information
tmolitor-stud-tu authored Oct 13, 2023
2 parents c9b6f3a + 3894979 commit 35ce130
Show file tree
Hide file tree
Showing 50 changed files with 1,041 additions and 871 deletions.
213 changes: 126 additions & 87 deletions Monal/Classes/AVCallUI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct VideoView: UIViewRepresentable {
}

struct AVCallUI: View {
@StateObject private var appDelegate: ObservableKVOWrapper<MonalAppDelegate>
@StateObject private var call: ObservableKVOWrapper<MLCall>
@StateObject private var contact: ObservableKVOWrapper<MLContact>
@State private var showMicAlert = false
Expand All @@ -35,16 +36,15 @@ struct AVCallUI: View {
private var busyPlayer: AVAudioPlayer!
private var errorPlayer: AVAudioPlayer!
private var delegate: SheetDismisserProtocol
private var appDelegate: MonalAppDelegate
private var formatter: DateComponentsFormatter
private var localRenderer: RTCMTLVideoView
private var remoteRenderer: RTCMTLVideoView

init(delegate: SheetDismisserProtocol, call: MLCall) {
_call = StateObject(wrappedValue: ObservableKVOWrapper(call))
_contact = StateObject(wrappedValue: ObservableKVOWrapper(call.contact))
_appDelegate = StateObject(wrappedValue: ObservableKVOWrapper(UIApplication.shared.delegate as! MonalAppDelegate))
self.delegate = delegate
self.appDelegate = UIApplication.shared.delegate as! MonalAppDelegate
self.formatter = DateComponentsFormatter()
self.formatter.allowedUnits = [.hour, .minute, .second]
self.formatter.unitsStyle = .positional
Expand All @@ -63,6 +63,99 @@ struct AVCallUI: View {
self.busyPlayer = try! AVAudioPlayer(contentsOf:Bundle.main.url(forResource:"busy", withExtension:"wav", subdirectory:"CallSounds")!)
self.errorPlayer = try! AVAudioPlayer(contentsOf:Bundle.main.url(forResource:"error", withExtension:"wav", subdirectory:"CallSounds")!)
}

func handleStateChange(_ state:MLCallState, _ audioState:MLAudioState) {
switch state {
case .unknown:
DDLogDebug("state: unknown")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.play()
case .discovering:
DDLogDebug("state: discovering")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.stop()
case .ringing:
DDLogDebug("state: ringing")
busyPlayer.stop()
errorPlayer.stop()
ringingPlayer.play()
case .connecting:
DDLogDebug("state: connecting")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.stop()
case .reconnecting:
DDLogDebug("state: reconnecting")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.stop()
case .connected:
DDLogDebug("state: connected")
if MLCallType(rawValue:call.callType) == .video {
call.obj.startCaptureLocalVideo(withRenderer: self.localRenderer)
call.obj.renderRemoteVideo(withRenderer: self.remoteRenderer)
}
case .finished:
DDLogDebug("state: finished: \(String(describing:call.finishReason as NSNumber))")
//check audio state before trying to play anything (if we are still in state .call,
//callkit will deactivate this audio session shortly, stopping our players)
if audioState == .normal {
switch MLCallFinishReason(rawValue:call.finishReason) {
case .unknown:
DDLogDebug("state: finished: unknown")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.stop()
case .connectivityError:
DDLogDebug("state: finished: connectivityError")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.play()
case .securityError:
DDLogDebug("state: finished: securityError")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.play()
case .unanswered:
DDLogDebug("state: finished: unanswered")
ringingPlayer.stop()
errorPlayer.stop()
busyPlayer.play()
case .retracted:
DDLogDebug("state: finished: retracted")
ringingPlayer.stop()
errorPlayer.stop()
busyPlayer.play()
case .rejected:
DDLogDebug("state: finished: rejected")
ringingPlayer.stop()
errorPlayer.stop()
busyPlayer.play()
case .declined:
DDLogDebug("state: finished: declined")
ringingPlayer.stop()
errorPlayer.stop()
busyPlayer.play()
case .error:
DDLogDebug("state: finished: error")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.play()
// case .normal:
// case .answeredElsewhere:
default:
DDLogDebug("state: finished: default")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.stop()
}
}
default:
DDLogDebug("state: default")
}
}

var body: some View {
ZStack {
Expand Down Expand Up @@ -152,7 +245,7 @@ struct AVCallUI: View {
Spacer().frame(height: 8)
Button(action: {
self.delegate.dismissWithoutAnimation()
if let activeChats = self.appDelegate.activeChats {
if let activeChats = self.appDelegate.obj.activeChats {
activeChats.presentChat(with:self.contact.obj)
}
}, label: {
Expand Down Expand Up @@ -198,13 +291,25 @@ struct AVCallUI: View {
.bold()
.foregroundColor(.primary)
case .normal:
Text("Call ended, duration: \(formatter.string(from: TimeInterval(call.durationTime as UInt))!)")
.bold()
.foregroundColor(.primary)
if call.wasConnectedOnce {
Text("Call ended, duration: \(formatter.string(from: TimeInterval(call.durationTime as UInt))!)")
.bold()
.foregroundColor(.primary)
} else {
Text("Call ended")
.bold()
.foregroundColor(.primary)
}
case .connectivityError:
Text("Call ended: connection failed")
.bold()
.foregroundColor(.primary)
if call.wasConnectedOnce {
Text("Call ended: connection failed\nDuration: \(formatter.string(from: TimeInterval(call.durationTime as UInt))!)")
.bold()
.foregroundColor(.primary)
} else {
Text("Call ended: connection failed")
.bold()
.foregroundColor(.primary)
}
case .securityError:
Text("Call ended: couldn't establish encryption")
.bold()
Expand Down Expand Up @@ -260,7 +365,7 @@ struct AVCallUI: View {

Button(action: {
self.delegate.dismissWithoutAnimation()
if let activeChats = self.appDelegate.activeChats {
if let activeChats = self.appDelegate.obj.activeChats {
activeChats.call(contact.obj)
}
}) {
Expand Down Expand Up @@ -460,7 +565,7 @@ struct AVCallUI: View {
.onAppear {
//force portrait mode and lock ui there
UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
self.appDelegate.orientationLock = .portrait
self.appDelegate.obj.orientationLock = .portrait
self.ringingPlayer.numberOfLoops = -1
self.busyPlayer.numberOfLoops = -1
self.errorPlayer.numberOfLoops = -1
Expand All @@ -474,84 +579,18 @@ struct AVCallUI: View {
}
.onDisappear {
//allow all orientations again
self.appDelegate.orientationLock = .all
self.appDelegate.obj.orientationLock = .all
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.stop()
}
.onChange(of: MLCallState(rawValue:call.state)) { state in
DDLogVerbose("state changed: \(String(describing:call.state as NSNumber))")
switch state {
// case .discovering:
case .ringing:
DDLogDebug("state: ringing")
busyPlayer.stop()
errorPlayer.stop()
ringingPlayer.play()
// case .connecting:
// case .reconnecting:
case .connected:
DDLogDebug("state: connected")
if MLCallType(rawValue:call.callType) == .video {
call.obj.startCaptureLocalVideo(withRenderer: self.localRenderer)
call.obj.renderRemoteVideo(withRenderer: self.remoteRenderer)
}
case .finished:
DDLogDebug("state: finished: \(String(describing:call.finishReason as NSNumber))")
switch MLCallFinishReason(rawValue:call.finishReason) {
case .unknown:
DDLogDebug("state: finished: unknown")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.play()
// case .normal:
case .connectivityError:
DDLogDebug("state: finished: connectivityError")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.play()
case .securityError:
DDLogDebug("state: finished: securityError")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.play()
case .unanswered:
DDLogDebug("state: finished: unanswered")
ringingPlayer.stop()
errorPlayer.stop()
busyPlayer.play()
// case .answeredElsewhere:
case .retracted:
DDLogDebug("state: finished: retracted")
//this will only be displayed for timer-induced retractions,
//reflect that in our text instead of using some generic "hung up"
ringingPlayer.stop()
errorPlayer.stop()
busyPlayer.play()
case .rejected:
DDLogDebug("state: finished: rejected")
ringingPlayer.stop()
errorPlayer.stop()
busyPlayer.play()
case .declined:
DDLogDebug("state: finished: declined")
ringingPlayer.stop()
errorPlayer.stop()
busyPlayer.play()
case .error:
DDLogDebug("state: finished: error")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.play()
default:
DDLogDebug("state: finished: default")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.stop()
}
default:
DDLogDebug("state: default")
ringingPlayer.stop()
busyPlayer.stop()
errorPlayer.stop()
}
DDLogVerbose("call state changed: \(String(describing:call.state as NSNumber))")
handleStateChange(call.obj.state, appDelegate.obj.audioState)
}
.onChange(of: MLAudioState(rawValue:appDelegate.audioState)) { audioState in
DDLogVerbose("audioState changed: \(String(describing:appDelegate.audioState as NSNumber))")
handleStateChange(call.obj.state, appDelegate.obj.audioState)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Monal/Classes/ActiveChatsViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ NS_ASSUME_NONNULL_BEGIN
-(void) showPrivacySettings;
-(void) showDetails;
-(void) showRegisterWithUsername:(NSString*) username onHost:(NSString*) host withToken:(NSString* _Nullable) token usingCompletion:(monal_id_block_t _Nullable) callback;
-(void) showAddContactWithJid:(NSString*) jid andPreauthToken:(NSString* _Nullable) preauthToken;
-(void) showAddContactWithJid:(NSString*) jid preauthToken:(NSString* _Nullable) preauthToken prefillAccount:(xmpp* _Nullable) account andOmemoFingerprints:(NSDictionary* _Nullable) fingerprints;

@end

Expand Down
19 changes: 11 additions & 8 deletions Monal/Classes/ActiveChatsViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -428,17 +428,20 @@ -(void) viewDidAppear:(BOOL) animated
-(void) didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

-(void) showAddContactWithJid:(NSString*) jid andPreauthToken:(NSString* _Nullable) preauthToken
-(void) showAddContactWithJid:(NSString*) jid preauthToken:(NSString* _Nullable) preauthToken prefillAccount:(xmpp* _Nullable) account andOmemoFingerprints:(NSDictionary* _Nullable) fingerprints
{
UIViewController* addContactMenuView = [[SwiftuiInterface new] makeAddContactViewForJid:jid andPreauthToken:preauthToken withDismisser:^(MLContact* _Nonnull newContact) {
dispatch_async(dispatch_get_main_queue(), ^{
[self presentChatWithContact:newContact];
});
}];
[self presentViewController:addContactMenuView animated:YES completion:^{}];
dispatch_async(dispatch_get_main_queue(), ^{
[self dismissCompleteViewChainWithAnimation:NO andCompletion:^{
UIViewController* addContactMenuView = [[SwiftuiInterface new] makeAddContactViewForJid:jid preauthToken:preauthToken prefillAccount:account andOmemoFingerprints:fingerprints withDismisser:^(MLContact* _Nonnull newContact) {
dispatch_async(dispatch_get_main_queue(), ^{
[self presentChatWithContact:newContact];
});
}];
[self presentViewController:addContactMenuView animated:NO completion:^{}];
}];
});
}

-(void) segueToIntroScreensIfNeeded
Expand Down
Loading

0 comments on commit 35ce130

Please sign in to comment.