diff --git a/Classes/ChatConversationView.m b/Classes/ChatConversationView.m index 6ae1f25b4..50d89e1a8 100644 --- a/Classes/ChatConversationView.m +++ b/Classes/ChatConversationView.m @@ -1344,7 +1344,7 @@ void on_chat_room_chat_message_received(LinphoneChatRoom *cr, const LinphoneEven if ((linphone_core_get_max_size_for_auto_download_incoming_files(LC) > -1) && linphone_chat_message_get_file_transfer_information(chat)) hasFile = TRUE; - if (!linphone_chat_message_is_file_transfer(chat) && !linphone_chat_message_is_text(chat) && !hasFile) /*probably an imdn*/ + if (!linphone_chat_message_is_file_transfer(chat) && !linphone_chat_message_is_text(chat) && !hasFile && ![ICSBubbleView isConferenceInvitationMessageWithCmessage:chat]) /*probably an imdn*/ return; const LinphoneAddress *from = linphone_chat_message_get_from_address(chat); diff --git a/Classes/LinphoneManager.m b/Classes/LinphoneManager.m index 59fc88be5..afcd795e0 100644 --- a/Classes/LinphoneManager.m +++ b/Classes/LinphoneManager.m @@ -862,7 +862,7 @@ static void linphone_iphone_popup_password_request(LinphoneCore *lc, LinphoneAut if ((linphone_core_get_max_size_for_auto_download_incoming_files(LC) > -1) && linphone_chat_message_get_file_transfer_information(msg)) hasFile = TRUE; - if (!linphone_chat_message_is_file_transfer(msg) && !linphone_chat_message_is_text(msg) && !hasFile) + if (!linphone_chat_message_is_file_transfer(msg) && !linphone_chat_message_is_text(msg) && !hasFile && ![ICSBubbleView isConferenceInvitationMessageWithCmessage:msg]) return; if (hasFile) { diff --git a/Classes/LinphoneUI/UICamSwitch.m b/Classes/LinphoneUI/UICamSwitch.m index ce88d3120..22218a0e5 100644 --- a/Classes/LinphoneUI/UICamSwitch.m +++ b/Classes/LinphoneUI/UICamSwitch.m @@ -54,10 +54,6 @@ INIT_WITH_COMMON_CF { if (newCamId) { LOGI(@"Switching from [%s] to [%s]", currentCamId, newCamId); linphone_core_set_video_device(LC, newCamId); - LinphoneCall *call = linphone_core_get_current_call(LC); - if (call != NULL) { - linphone_core_update_call(LC, call, NULL); - } } } diff --git a/Classes/Swift/Conference/models/ConferenceSchedulingViewModel.swift b/Classes/Swift/Conference/models/ConferenceSchedulingViewModel.swift index 22eb92419..458106791 100644 --- a/Classes/Swift/Conference/models/ConferenceSchedulingViewModel.swift +++ b/Classes/Swift/Conference/models/ConferenceSchedulingViewModel.swift @@ -24,7 +24,7 @@ import linphonesw class ConferenceSchedulingViewModel { - let core = Core.get() + var core : Core { get { Core.get() } } static let shared = ConferenceSchedulingViewModel() let subject = MutableLiveData() diff --git a/Classes/Swift/Conference/models/ScheduledConferencesViewModel.swift b/Classes/Swift/Conference/models/ScheduledConferencesViewModel.swift index 15a69d549..72449a192 100644 --- a/Classes/Swift/Conference/models/ScheduledConferencesViewModel.swift +++ b/Classes/Swift/Conference/models/ScheduledConferencesViewModel.swift @@ -25,7 +25,7 @@ import linphonesw class ScheduledConferencesViewModel { - let core = Core.get() + var core : Core { get { Core.get() } } static let shared = ScheduledConferencesViewModel() var conferences : MutableLiveData<[ScheduledConferenceData]> = MutableLiveData([]) diff --git a/Classes/Swift/Conference/views/ConferenceSchedulingView.swift b/Classes/Swift/Conference/views/ConferenceSchedulingView.swift index a64402d96..372ee752c 100644 --- a/Classes/Swift/Conference/views/ConferenceSchedulingView.swift +++ b/Classes/Swift/Conference/views/ConferenceSchedulingView.swift @@ -46,11 +46,10 @@ import linphonesw contentView.addSubview(subjectLabel) subjectLabel.alignParentLeft(withMargin: form_margin).alignParentTop().done() - let subjectInput = StyledTextView(VoipTheme.conference_scheduling_font, placeHolder:VoipTexts.conference_schedule_subject_hint, liveValue: viewModel.subject) + let subjectInput = StyledTextView(VoipTheme.conference_scheduling_font, placeHolder:VoipTexts.conference_schedule_subject_hint, liveValue: viewModel.subject,maxLines:1) contentView.addSubview(subjectInput) subjectInput.alignUnder(view: subjectLabel,withMargin: form_margin).matchParentSideBorders(insetedByDx: form_margin).height(form_input_height).done() - let schedulingStack = UIStackView() schedulingStack.axis = .vertical schedulingStack.backgroundColor = VoipTheme.voipFormBackgroundColor.get() diff --git a/Classes/Swift/Conference/views/ConferenceWaitingRoomFragment.swift b/Classes/Swift/Conference/views/ConferenceWaitingRoomFragment.swift index 867ca5f31..690338032 100644 --- a/Classes/Swift/Conference/views/ConferenceWaitingRoomFragment.swift +++ b/Classes/Swift/Conference/views/ConferenceWaitingRoomFragment.swift @@ -91,6 +91,7 @@ import linphonesw // localVideo view localVideo.layer.cornerRadius = center_view_corner_radius localVideo.clipsToBounds = true + localVideo.contentMode = .scaleAspectFit localVideo.backgroundColor = .black self.view.addSubview(localVideo) localVideo.matchParentSideBorders(insetedByDx: content_inset).alignAbove(view:buttonsView,withMargin:SharedLayoutConstants.buttons_bottom_margin).alignUnder(view: subject,withMargin: common_margin).done() diff --git a/Classes/Swift/Extensions/IOS/UITextViewExtensions.swift b/Classes/Swift/Extensions/IOS/UITextViewExtensions.swift new file mode 100644 index 000000000..a5341619e --- /dev/null +++ b/Classes/Swift/Extensions/IOS/UITextViewExtensions.swift @@ -0,0 +1,35 @@ +/* +* Copyright (c) 2010-2020 Belledonne Communications SARL. +* +* This file is part of linhome +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + + + +import Foundation +import UIKit +import AVFoundation + +extension UITextView { + var numberOfCurrentlyDisplayedLines: Int { + return text.components(separatedBy: "\n").count + } + func removeTextUntilSatisfying(maxNumberOfLines: Int) { + while numberOfCurrentlyDisplayedLines > (maxNumberOfLines) { + text = String(text.dropLast()) + } + } +} diff --git a/Classes/Swift/Extensions/LinphoneCore/CoreExtensions.swift b/Classes/Swift/Extensions/LinphoneCore/CoreExtensions.swift index f9a4c15f1..5de3e26d7 100644 --- a/Classes/Swift/Extensions/LinphoneCore/CoreExtensions.swift +++ b/Classes/Swift/Extensions/LinphoneCore/CoreExtensions.swift @@ -42,10 +42,6 @@ extension Core { return } } - - let inConference = conference != nil && conference!.isIn - if !inConference, let call = currentCall { - try?call.update(params: nil) - }*/ + */ } } diff --git a/Classes/Swift/Voip/AudioRouteUtils.swift b/Classes/Swift/Voip/AudioRouteUtils.swift index 94a26f71e..91944d7e9 100644 --- a/Classes/Swift/Voip/AudioRouteUtils.swift +++ b/Classes/Swift/Voip/AudioRouteUtils.swift @@ -24,8 +24,8 @@ import linphonesw @objc class AudioRouteUtils : NSObject { - static let core = Core.get() - + static var core : Core { get { Core.get() } } + static private func applyAudioRouteChange( call: Call?, types: [AudioDeviceType], output: Bool = true) { let typesNames = types.map { String(describing: $0) }.joined(separator: "/") diff --git a/Classes/Swift/Voip/Models/CallsViewModel.swift b/Classes/Swift/Voip/Models/CallsViewModel.swift index b539b8e5c..b68707700 100644 --- a/Classes/Swift/Voip/Models/CallsViewModel.swift +++ b/Classes/Swift/Voip/Models/CallsViewModel.swift @@ -33,8 +33,8 @@ class CallsViewModel { let callConnectedEvent = MutableLiveData() let callUpdateEvent = MutableLiveData() let noMoreCallEvent = MutableLiveData(false) - let core = Core.get() - + var core : Core { get { Core.get() } } + static let shared = CallsViewModel() private var coreDelegate : CoreDelegateStub? diff --git a/Classes/Swift/Voip/Models/ConferenceParticipantDeviceData.swift b/Classes/Swift/Voip/Models/ConferenceParticipantDeviceData.swift index b10dd635f..1756d6cb2 100644 --- a/Classes/Swift/Voip/Models/ConferenceParticipantDeviceData.swift +++ b/Classes/Swift/Voip/Models/ConferenceParticipantDeviceData.swift @@ -28,7 +28,7 @@ class ConferenceParticipantDeviceData { let videoEnabled = MutableLiveData() let activeSpeaker = MutableLiveData() let isInConference = MutableLiveData() - let core = Core.get() + var core : Core { get { Core.get() } } private var participantDeviceDelegate : ParticipantDeviceDelegate? @@ -45,28 +45,43 @@ class ConferenceParticipantDeviceData { }, onConferenceLeft: { (participantDevice) in Log.i("[Conference Participant Device] Participant \(participantDevice) has left the conference") self.isInConference.value = false - }, onAudioDirectionChanged: { (participantDevice, direction) in - Log.i("[Conference Participant Device] Participant \(participantDevice) audio stream direction changed: \(direction)") - }, onVideoDirectionChanged: { (participantDevice, direction) in + }, onStreamCapabilityChanged: { (participantDevice, direction, streamType) in Log.i("[Conference Participant Device] Participant \(participantDevice) video stream direction changed: \(direction)") self.videoEnabled.value = direction == MediaDirection.SendOnly || direction == MediaDirection.SendRecv - }, onTextDirectionChanged: { (participantDevice, direction) in - Log.i("[Conference Participant Device] Participant \(participantDevice) text stream direction changed: \(direction)") - }) + if (streamType == StreamType.Video) { + Log.i("[Conference Participant Device] Participant [\(participantDevice.address?.asStringUriOnly())] video capability changed to \(direction)") + } + }, onStreamAvailabilityChanged: { (participantDevice, available, streamType) in + if (streamType == StreamType.Video) { + Log.i("[Conference Participant Device] Participant [\(participantDevice.address?.asStringUriOnly())] video availability changed to \(available)") + self.videoEnabled.value = available + } + } + + ) participantDevice.addDelegate(delegate: participantDeviceDelegate!) activeSpeaker.value = false - // TODO: What happens if we have disabled video locally? - videoEnabled.value = participantDevice.videoDirection == MediaDirection.SendOnly || participantDevice.videoDirection == MediaDirection.SendRecv - isInConference.value = participantDevice.isInConference + videoEnabled.value = participantDevice.getStreamAvailability(streamType: .Video) + + isInConference.value = participantDevice.isInConference + let videoCapability = participantDevice.getStreamCapability(streamType: .Video) + Log.i("[Conference Participant Device] Participant [\(participantDevice.address?.asStringUriOnly())], is in conf? \(isInConference.value), is video enabled? \(videoEnabled.value) \(videoCapability)") } func destroy() { + clearObservers() participantDevice.removeDelegate(delegate: participantDeviceDelegate!) } + func clearObservers() { + isInConference.clearObservers() + videoEnabled.clearObservers() + activeSpeaker.clearObservers() + } + func switchCamera() { Core.get().toggleCamera() } @@ -76,12 +91,10 @@ class ConferenceParticipantDeviceData { } func setVideoView(view:UIView) { - if (!isMe && participantDevice.videoDirection != MediaDirection.SendRecv) { - Log.e("[Conference Participant Device] Participant \(participantDevice) device video direction is \(participantDevice.videoDirection), don't set video window!") - return - } Log.i("[Conference Participant Device] Setting textureView \(view) for participant \(participantDevice)") - if (isMe) { // TODO: remove + if (isMe) { + core.usePreviewWindow(yesno: false) + view.contentMode = .scaleAspectFit core.nativePreviewWindow = view } else { participantDevice.nativeVideoWindowId = UnsafeMutableRawPointer(Unmanaged.passRetained(view).toOpaque()) diff --git a/Classes/Swift/Voip/Models/ConferenceViewModel.swift b/Classes/Swift/Voip/Models/ConferenceViewModel.swift index 0b0f4601a..63af09c47 100644 --- a/Classes/Swift/Voip/Models/ConferenceViewModel.swift +++ b/Classes/Swift/Voip/Models/ConferenceViewModel.swift @@ -24,7 +24,7 @@ import AVFoundation class ConferenceViewModel { - let core = Core.get() + var core : Core { get { Core.get() } } static let shared = ConferenceViewModel() let conferenceExists = MutableLiveData() @@ -81,7 +81,7 @@ class ConferenceViewModel { coreDelegate = CoreDelegateStub( onConferenceStateChanged: { (core, conference, state) in Log.i("[Conference] \(conference) Conference state changed: \(state)") - self.isVideoConference.value = conference.currentParams?.isVideoEnabled == true + self.isVideoConference.value = conference.currentParams?.videoEnabled == true if (state == Conference.State.Instantiated) { self.initConference(conference) @@ -135,7 +135,7 @@ class ConferenceViewModel { isConferenceLocallyPaused.value = !conference.isIn self.isMeAdmin.value = conference.me?.isAdmin == true - isVideoConference.value = conference.currentParams?.isVideoEnabled == true + isVideoConference.value = conference.currentParams?.videoEnabled == true self.subject.value = conference.subject.isEmpty ? ( conference.me?.isFocus == true ? ( diff --git a/Classes/Swift/Voip/Models/ControlsViewModel.swift b/Classes/Swift/Voip/Models/ControlsViewModel.swift index 53029a73e..cfc333f14 100644 --- a/Classes/Swift/Voip/Models/ControlsViewModel.swift +++ b/Classes/Swift/Voip/Models/ControlsViewModel.swift @@ -24,8 +24,8 @@ import AVFoundation class ControlsViewModel { - let core = Core.get() - + var core : Core { get { Core.get() } } + let isSpeakerSelected = MutableLiveData() let isMicrophoneMuted = MutableLiveData() let isMuteMicrophoneEnabled = MutableLiveData() @@ -138,7 +138,7 @@ class ControlsViewModel { func toggleVideo() { if let conference = core.conference, conference.isIn { if let params = try?core.createConferenceParams() { - let videoEnabled = conference.currentParams?.isVideoEnabled == true + let videoEnabled = conference.currentParams?.videoEnabled == true params.videoEnabled = !videoEnabled _ = conference.updateParams(params: params) } diff --git a/Classes/Swift/Voip/Theme/TextStyle.swift b/Classes/Swift/Voip/Theme/TextStyle.swift index bf1f93c30..5e242ae43 100644 --- a/Classes/Swift/Voip/Theme/TextStyle.swift +++ b/Classes/Swift/Voip/Theme/TextStyle.swift @@ -89,5 +89,13 @@ extension UITextView { let fontSizeMultiplier: Float = (UIDevice.ipad() ? 1.25 : UIDevice.is5SorSEGen1() ? 0.9 : 1.0) font = UIFont.init(name: style.font, size: CGFloat(style.size*fontSizeMultiplier)) } + var numberOfCurrentlyDisplayedLines: Int { + return text.components(separatedBy: "\n").count + } + func removeTextUntilSatisfying(maxNumberOfLines: Int) { + while numberOfCurrentlyDisplayedLines > (maxNumberOfLines) { + text = String(text.dropLast()) + } + } } diff --git a/Classes/Swift/Voip/Views/Fragments/Conference/VoipGridParticipantCell.swift b/Classes/Swift/Voip/Views/Fragments/Conference/VoipGridParticipantCell.swift index 6339a0d15..79ebb474c 100644 --- a/Classes/Swift/Voip/Views/Fragments/Conference/VoipGridParticipantCell.swift +++ b/Classes/Swift/Voip/Views/Fragments/Conference/VoipGridParticipantCell.swift @@ -57,6 +57,10 @@ class VoipGridParticipantCell: UICollectionViewCell { } self.switchCamera.isHidden = videoEnabled != true || !data.isSwitchCameraAvailable() } + if (data.participantDevice.address == nil) { + avatar.isHidden = true + } + self.displayName.text = "" data.participantDevice.address.map { avatar.fillFromAddress(address: $0) if let displayName = $0.addressBookEnhancedDisplayName() { diff --git a/Classes/Swift/Voip/Widgets/StyledTextView.swift b/Classes/Swift/Voip/Widgets/StyledTextView.swift index 190203675..6e2f54984 100644 --- a/Classes/Swift/Voip/Widgets/StyledTextView.swift +++ b/Classes/Swift/Voip/Widgets/StyledTextView.swift @@ -24,15 +24,19 @@ class StyledTextView: UITextView, UITextViewDelegate { var placeholder:String? var style:TextStyle? var liveValue: MutableLiveData? = nil + var maxLines:Int required init?(coder: NSCoder) { + maxLines = 0 super.init(coder: coder) } - init (_ style:TextStyle, placeHolder:String? = nil, liveValue: MutableLiveData, readOnly:Bool = false) { + init (_ style:TextStyle, placeHolder:String? = nil, liveValue: MutableLiveData, readOnly:Bool = false, maxLines:Int = 999) { + self.maxLines = maxLines self.style = style self.liveValue = liveValue super.init(frame:.zero, textContainer: nil) + textContainer.maximumNumberOfLines = maxLines applyStyle(style) setFormInputBackground(readOnly:readOnly) placeHolder.map { @@ -43,6 +47,7 @@ class StyledTextView: UITextView, UITextViewDelegate { self.text = value if (value == nil || value?.count == 0) { self.showPlaceHolder() + self.resignFirstResponder() } } if (readOnly) { @@ -71,6 +76,7 @@ class StyledTextView: UITextView, UITextViewDelegate { } func textViewDidChange(_ textView: UITextView) { + textView.removeTextUntilSatisfying(maxNumberOfLines: self.maxLines) liveValue?.value = textView.text } diff --git a/Classes/Swift/Voip/Widgets/StyledValuePicker.swift b/Classes/Swift/Voip/Widgets/StyledValuePicker.swift index a8bd5b857..f8ca9d3c6 100644 --- a/Classes/Swift/Voip/Widgets/StyledValuePicker.swift +++ b/Classes/Swift/Voip/Widgets/StyledValuePicker.swift @@ -41,8 +41,8 @@ class StyledValuePicker: UIView { formattedLabel.isUserInteractionEnabled = false formattedLabel.backgroundColor = VoipTheme.voipFormBackgroundColor.get() - formattedLabel.text = " "+options[liveIndex.value!] - + liveIndex.value.map { formattedLabel.text = " "+options[$0] } + if (readOnly) { formattedLabel.textColor = formattedLabel.textColor.withAlphaComponent(0.5) } diff --git a/Podfile b/Podfile index d8ffbb540..6affc64ca 100644 --- a/Podfile +++ b/Podfile @@ -5,7 +5,7 @@ source "https://github.com/CocoaPods/Specs.git" def all_pods if ENV['PODFILE_PATH'].nil? - pod 'linphone-sdk', '~> 5.1.0-alpha.75+d4a0bd2' + pod 'linphone-sdk', '~> 5.2.0-alpha.125+0f20296' else pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk end diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj index 031fbca04..5f2485bfe 100644 --- a/linphone.xcodeproj/project.pbxproj +++ b/linphone.xcodeproj/project.pbxproj @@ -5642,7 +5642,7 @@ "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"5.2.0-alpha.94+f4cb5df\\\"", + "-DLINPHONE_SDK_VERSION=\\\"5.2.0-alpha.125+0f20296\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; @@ -5769,7 +5769,7 @@ "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"5.2.0-alpha.94+f4cb5df\\\"", + "-DLINPHONE_SDK_VERSION=\\\"5.2.0-alpha.125+0f20296\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; @@ -5895,7 +5895,7 @@ "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"5.2.0-alpha.94+f4cb5df\\\"", + "-DLINPHONE_SDK_VERSION=\\\"5.2.0-alpha.125+0f20296\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; @@ -6020,7 +6020,7 @@ "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"5.2.0-alpha.94+f4cb5df\\\"", + "-DLINPHONE_SDK_VERSION=\\\"5.2.0-alpha.125+0f20296\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; diff --git a/msgNotificationService/NotificationService.swift b/msgNotificationService/NotificationService.swift index c657f797c..a47699e24 100644 --- a/msgNotificationService/NotificationService.swift +++ b/msgNotificationService/NotificationService.swift @@ -149,7 +149,7 @@ class NotificationService: UNNotificationServiceExtension { } func parseMessage(message: PushNotificationMessage) -> MsgData? { - let content = message.isText ? message.textContent : "🗻" + let content = message.isIcalendar ? NSLocalizedString("You are invited to a conference", comment: "") : message.isText ? message.textContent : "🗻" let fromAddr = message.fromAddr?.username let callId = message.callId let localUri = message.localAddr?.asStringUriOnly()