Skip to content

Commit

Permalink
Merge pull request #951 from monal-im/6.0b15
Browse files Browse the repository at this point in the history
- Add support for OMEMO-verified calls
- Ask for mic permission when starting a call
- Allow bluetooth headsets in calls
- Add XEP-0215 (external services) to server details ui
- Severall smaller fixes in our audio call implementation
- Monal calls are compatible with Dino now
  • Loading branch information
tmolitor-stud-tu authored Oct 3, 2023
2 parents cfafee2 + 5e33b0b commit c9b6f3a
Show file tree
Hide file tree
Showing 33 changed files with 1,184 additions and 374 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/stable.build-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ jobs:
run: ./scripts/uploadNonAlpha.sh stable
- name: Publish catalyst to appstore connect
run: xcrun altool --upload-app --file ./Monal/build/app/Monal.pkg --type macos --asc-provider S8D843U34Y -u "$(cat /Users/ci/apple_connect_upload_mail.txt)" -p "$(cat /Users/ci/apple_connect_upload_secret.txt)" --primary-bundle-id maccatalyst.G7YU7X7KRJ.SworIM
- name: Update xmpp.org client list with new timestamp
run: ./scripts/push_xmpp.org.sh
# - name: Update xmpp.org client list with new timestamp
# run: ./scripts/push_xmpp.org.sh
- uses: actions/upload-artifact@v2
with:
name: monal-catalyst-pkg
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
!rust/Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk
Expand Down
158 changes: 131 additions & 27 deletions Monal/Classes/AVCallUI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ struct VideoView: UIViewRepresentable {
struct AVCallUI: View {
@StateObject private var call: ObservableKVOWrapper<MLCall>
@StateObject private var contact: ObservableKVOWrapper<MLContact>
@State private var showMicAlert = false
@State private var showSecurityHelpAlert: MLCallEncryptionState? = nil
private var ringingPlayer: AVAudioPlayer!
private var busyPlayer: AVAudioPlayer!
private var errorPlayer: AVAudioPlayer!
Expand Down Expand Up @@ -84,41 +86,84 @@ struct AVCallUI: View {
Group {
Spacer().frame(height: 24)

HStack {
switch MLCallDirection(rawValue:call.direction) {
case .incoming:
Image(systemName: "phone.arrow.down.left")
.resizable()
.frame(width: 20.0, height: 20.0)
.foregroundColor(.primary)
case .outgoing:
Image(systemName: "phone.arrow.up.right")
.resizable()
.frame(width: 20.0, height: 20.0)
.foregroundColor(.primary)
default: //should never be reached
Text("")
HStack(alignment: .top) {
Spacer().frame(width:20)

VStack {
Spacer().frame(height: 8)
switch MLCallDirection(rawValue:call.direction) {
case .incoming:
Image(systemName: "phone.arrow.down.left")
.resizable()
.frame(width: 20.0, height: 20.0)
.foregroundColor(.primary)
case .outgoing:
Image(systemName: "phone.arrow.up.right")
.resizable()
.frame(width: 20.0, height: 20.0)
.foregroundColor(.primary)
default: //should never be reached
Text("")
}
}

VStack {
Spacer().frame(height: 8)
Button(action: {
//show dialog explaining different encryption states
self.showSecurityHelpAlert = MLCallEncryptionState(rawValue:call.encryptionState)
}, label: {
switch MLCallEncryptionState(rawValue:call.encryptionState) {
case .unknown:
Text("")
case .clear:
Spacer().frame(width: 10)
Image(systemName: "xmark.shield.fill")
.resizable()
.frame(width: 20.0, height: 20.0)
.foregroundColor(.red)
case .toFU:
Spacer().frame(width: 10)
Image(systemName: "checkmark.shield.fill")
.resizable()
.frame(width: 20.0, height: 20.0)
.foregroundColor(.yellow)
case .trusted:
Spacer().frame(width: 10)
Image(systemName: "checkmark.shield.fill")
.resizable()
.frame(width: 20.0, height: 20.0)
.foregroundColor(.green)
default: //should never be reached
Text("")
}
})
}

Spacer().frame(width: 20)
Spacer()

Text(contact.contactDisplayName as String)
.font(.largeTitle)
.foregroundColor(.primary)

Spacer().frame(width: 20)
Spacer()

Button(action: {
self.delegate.dismissWithoutAnimation()
if let activeChats = self.appDelegate.activeChats {
activeChats.presentChat(with:self.contact.obj)
}
}, label: {
Image(systemName: "text.bubble")
.resizable()
.frame(width: 28.0, height: 28.0)
.foregroundColor(.primary)
})
VStack {
Spacer().frame(height: 8)
Button(action: {
self.delegate.dismissWithoutAnimation()
if let activeChats = self.appDelegate.activeChats {
activeChats.presentChat(with:self.contact.obj)
}
}, label: {
Image(systemName: "text.bubble")
.resizable()
.frame(width: 28.0, height: 28.0)
.foregroundColor(.primary)
})
}

Spacer().frame(width:20)
}

Spacer().frame(height: 16)
Expand Down Expand Up @@ -160,6 +205,10 @@ struct AVCallUI: View {
Text("Call ended: connection failed")
.bold()
.foregroundColor(.primary)
case .securityError:
Text("Call ended: couldn't establish encryption")
.bold()
.foregroundColor(.primary)
case .unanswered:
Text("Call was not answered")
.bold()
Expand Down Expand Up @@ -365,13 +414,63 @@ struct AVCallUI: View {
Spacer().frame(height: 32)
}
}
.alert(isPresented: $showMicAlert) {
Alert(
title: Text("Missing permission"),
message: Text("You need to grant microphone access in iOS Settings-> Privacy-> Microphone, if you want that others can hear you."),
dismissButton: .default(Text("OK"))
)
}
.richAlert(isPresented:$showSecurityHelpAlert, title:Text("Call security help").foregroundColor(.black)) {
VStack(alignment: .leading) {
HStack {
Image(systemName: "xmark.shield.fill")
.resizable()
.frame(width: 20.0, height: 20.0)
.foregroundColor(.red)
Spacer().frame(width: 10)
Text("Red x-mark shield:")
}.font(Font.body.weight(showSecurityHelpAlert == .clear ? .heavy : .medium))
Text("This means your call is encrypted, but the remote party could not be verified using OMEMO encryption.\nYour or the callee's XMPP server could possibly Man-In-The-Middle you.")
Spacer().frame(height: 20)

HStack {
Image(systemName: "checkmark.shield.fill")
.resizable()
.frame(width: 20.0, height: 20.0)
.foregroundColor(.yellow)
Spacer().frame(width: 10)
Text("Yellow checkmark shield:")
}.font(Font.body.weight(showSecurityHelpAlert == .toFU ? .heavy : .medium))
Text("This means your call is encrypted and the remote party was verified using OMEMO encryption.\nBut since you did not manually verify the callee's OMEMO fingerprints, your or the callee's XMPP server could possibly have inserted their own OMEMO keys to Man-In-The-Middle you.")
Spacer().frame(height: 20)

HStack {
Image(systemName: "checkmark.shield.fill")
.resizable()
.frame(width: 20.0, height: 20.0)
.foregroundColor(.green)
Spacer().frame(width: 10)
Text("Green checkmark shield:")
}.font(Font.body.weight(showSecurityHelpAlert == .trusted ? .heavy : .medium))
Text("This means your call is encrypted and the remote party was verified using OMEMO encryption.\nYou manually verified the used OMEMO keys and no Man-In-The-Middle can take place.")
Spacer().frame(height: 20)
}.foregroundColor(.black)
}
.onAppear {
//force portrait mode and lock ui there
UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
self.appDelegate.orientationLock = .portrait
self.ringingPlayer.numberOfLoops = -1
self.busyPlayer.numberOfLoops = -1
self.errorPlayer.numberOfLoops = -1

//ask for mic permissions
AVAudioSession.sharedInstance().requestRecordPermission { granted in
if !granted {
showMicAlert = true
}
}
}
.onDisappear {
//allow all orientations again
Expand Down Expand Up @@ -408,6 +507,11 @@ struct AVCallUI: View {
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()
Expand Down
3 changes: 1 addition & 2 deletions Monal/Classes/HelperTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ NS_ASSUME_NONNULL_BEGIN
@class xmpp;
@class XMPPStanza;
@class UNNotificationRequest;
@class DDLogFormatter;
@class DDLogMessage;
@class MLFileLogger;
@class UIView;
Expand All @@ -40,7 +39,7 @@ void swizzle(Class c, SEL orig, SEL new);

@property (class, nonatomic, strong) MLFileLogger* fileLogger;

+(NSData* _Nullable) convertLogmessageToJsonData:(DDLogMessage*) logMessage usingFormatter:(id<DDLogFormatter> _Nullable) formatter counter:(uint64_t*) counter andError:(NSError** _Nullable) error;
+(NSData* _Nullable) convertLogmessageToJsonData:(DDLogMessage*) logMessage counter:(uint64_t*) counter andError:(NSError** _Nullable) error;
+(void) initSystem;
+(void) installExceptionHandler;
+(int) pendingCrashreportCount;
Expand Down
Loading

0 comments on commit c9b6f3a

Please sign in to comment.