Add calls list in call view

This commit is contained in:
Benoit Martins 2024-01-23 16:51:15 +01:00
parent 55631bf4f4
commit 05cc8277d8
7 changed files with 156 additions and 176 deletions

View file

@ -96,7 +96,6 @@
D7E6D0552AEBFCCE00A57AAF /* ContactsInnerFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E6D0542AEBFCCE00A57AAF /* ContactsInnerFragment.swift */; };
D7EAACCF2AD6ED8000AA6A8A /* PermissionsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EAACCE2AD6ED8000AA6A8A /* PermissionsFragment.swift */; };
D7F4D9CB2B5FD27200CDCD76 /* CallsListFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F4D9CA2B5FD27200CDCD76 /* CallsListFragment.swift */; };
D7F4D9CD2B5FD83A00CDCD76 /* CallsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F4D9CC2B5FD83A00CDCD76 /* CallsListViewModel.swift */; };
D7FB55112AD447FD00A5AB15 /* RegisterFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FB55102AD447FD00A5AB15 /* RegisterFragment.swift */; };
/* End PBXBuildFile section */
@ -193,7 +192,6 @@
D7E6D0542AEBFCCE00A57AAF /* ContactsInnerFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsInnerFragment.swift; sourceTree = "<group>"; };
D7EAACCE2AD6ED8000AA6A8A /* PermissionsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionsFragment.swift; sourceTree = "<group>"; };
D7F4D9CA2B5FD27200CDCD76 /* CallsListFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallsListFragment.swift; sourceTree = "<group>"; };
D7F4D9CC2B5FD83A00CDCD76 /* CallsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallsListViewModel.swift; sourceTree = "<group>"; };
D7FB55102AD447FD00A5AB15 /* RegisterFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterFragment.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -516,7 +514,6 @@
isa = PBXGroup;
children = (
D7B99E982B29B39000BE7BF2 /* CallViewModel.swift */,
D7F4D9CC2B5FD83A00CDCD76 /* CallsListViewModel.swift */,
);
path = ViewModel;
sourceTree = "<group>";
@ -715,7 +712,6 @@
D71FCA7F2AE1397200D2E43E /* ContactsListViewModel.swift in Sources */,
D71FCA812AE14CFC00D2E43E /* ContactsListFragment.swift in Sources */,
D719ABB72ABC67BF00B41C10 /* LinphoneApp.swift in Sources */,
D7F4D9CD2B5FD83A00CDCD76 /* CallsListViewModel.swift in Sources */,
D732A91B2B061BD900DB42BA /* HistoryListBottomSheet.swift in Sources */,
D72250632ADE9615008FB426 /* HistoryViewModel.swift in Sources */,
D726E4392B16440C0083C415 /* ContactAvatarModel.swift in Sources */,

View file

@ -140,6 +140,9 @@
},
"Accept all" : {
},
"Active" : {
},
"Add a picture" : {
@ -477,6 +480,9 @@
},
"Remove picture" : {
},
"Resuming" : {
},
"Say %@ and click on the letters given by your correspondent:" : {

View file

@ -113,6 +113,16 @@ class TelecomManager: ObservableObject {
}
}
func setHeldOtherCallsWithCore(exceptCallid: String) {
CoreContext.shared.doOnCoreQueue { core in
for call in core.calls {
if (call.callLog?.callId != exceptCallid && call.state != .Paused && call.state != .Pausing && call.state != .PausedByRemote) {
self.setHeld(call: call, hold: true)
}
}
}
}
func setHeldOtherCalls(core: Core, exceptCallid: String) {
for call in core.calls {
if (call.callLog?.callId != exceptCallid && call.state != .Paused && call.state != .Pausing && call.state != .PausedByRemote) {

View file

@ -48,6 +48,7 @@ struct CallView: View {
@State var showingDialer = false
@State var isShowCallsListFragment = false
@Binding var isShowStartCallFragment: Bool
var body: some View {
@ -104,6 +105,25 @@ struct CallView: View {
)
} onDismiss: {}
}
if isShowCallsListFragment {
CallsListFragment(callViewModel: callViewModel, isShowCallsListFragment: $isShowCallsListFragment)
.zIndex(4)
.transition(.move(edge: .bottom))
/*
.sheet(isPresented: $showingDialer) {
DialerBottomSheet(
startCallViewModel: startCallViewModel,
showingDialer: $showingDialer,
currentCall: nil
)
.presentationDetents([.medium])
// .interactiveDismissDisabled()
.presentationBackgroundInteraction(.enabled(upThrough: .medium))
}
*/
}
if callViewModel.zrtpPopupDisplayed == true {
ZRTPPopup(callViewModel: callViewModel)
.background(.black.opacity(0.65))
@ -116,6 +136,7 @@ struct CallView: View {
HStack {}
.onAppear {
callViewModel.resetCallView()
callViewModel.getCallsList()
}
}
}
@ -763,17 +784,20 @@ struct CallView: View {
VStack {
Button {
callViewModel.getCallsList()
withAnimation {
isShowCallsListFragment.toggle()
}
} label: {
Image("phone-list")
.renderingMode(.template)
.resizable()
.foregroundStyle(Color.gray500)
.foregroundStyle(.white)
.frame(width: 32, height: 32)
}
.frame(width: 60, height: 60)
.background(Color.gray600)
.background(Color.gray500)
.cornerRadius(40)
.disabled(true)
Text("Call list")
.foregroundStyle(.white)

View file

@ -21,7 +21,10 @@ import SwiftUI
struct CallsListFragment: View {
@ObservedObject var callsListViewModel: CallsListViewModel
@ObservedObject private var coreContext = CoreContext.shared
@ObservedObject private var contactsManager = ContactsManager.shared
@ObservedObject var callViewModel: CallViewModel
@State private var delayedColor = Color.white
@ -66,7 +69,7 @@ struct CallsListFragment: View {
.padding(.bottom, 4)
.background(.white)
//callsList
callsList
}
.background(.white)
}
@ -85,174 +88,145 @@ struct CallsListFragment: View {
}
}
/*
var callsList: some View {
VStack {
List {
ForEach(0..<historyListViewModel.callLogs.count, id: \.self) { index in
ForEach(0..<callViewModel.calls.count, id: \.self) { index in
HStack {
HStack {
let fromAddressFriend = contactsManager.getFriendWithAddress(address: historyListViewModel.callLogs[index].fromAddress!)
let toAddressFriend = contactsManager.getFriendWithAddress(address: historyListViewModel.callLogs[index].toAddress!)
let addressFriend = historyListViewModel.callLogs[index].dir == .Incoming ? fromAddressFriend : toAddressFriend
let contactAvatarModel = addressFriend != nil
? ContactsManager.shared.avatarListModel.first(where: {
($0.friend!.consolidatedPresence == .Online || $0.friend!.consolidatedPresence == .Busy)
&& $0.friend!.name == addressFriend!.name
&& $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly()
})
: ContactAvatarModel(friend: nil, withPresence: false)
if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty {
if contactAvatarModel != nil {
Avatar(contactAvatarModel: contactAvatarModel!, avatarSize: 45)
if callViewModel.calls[index].callLog != nil && callViewModel.calls[index].callLog!.remoteAddress != nil {
let addressFriend = contactsManager.getFriendWithAddress(address: callViewModel.calls[index].callLog!.remoteAddress!)
let contactAvatarModel = addressFriend != nil
? ContactsManager.shared.avatarListModel.first(where: {
($0.friend!.consolidatedPresence == .Online || $0.friend!.consolidatedPresence == .Busy)
&& $0.friend!.name == addressFriend!.name
&& $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly()
})
: ContactAvatarModel(friend: nil, withPresence: false)
if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty {
if contactAvatarModel != nil {
Avatar(contactAvatarModel: contactAvatarModel!, avatarSize: 45)
} else {
Image("profil-picture-default")
.resizable()
.frame(width: 45, height: 45)
.clipShape(Circle())
}
} else {
Image("profil-picture-default")
if callViewModel.calls[index].callLog!.remoteAddress!.displayName != nil {
Image(uiImage: contactsManager.textToImage(
firstName: callViewModel.calls[index].callLog!.remoteAddress!.displayName!,
lastName: callViewModel.calls[index].callLog!.remoteAddress!.displayName!.components(separatedBy: " ").count > 1
? callViewModel.calls[index].callLog!.remoteAddress!.displayName!.components(separatedBy: " ")[1]
: ""))
.resizable()
.frame(width: 45, height: 45)
.clipShape(Circle())
}
} else {
if historyListViewModel.callLogs[index].dir == .Outgoing && historyListViewModel.callLogs[index].toAddress != nil {
if historyListViewModel.callLogs[index].toAddress!.displayName != nil {
Image(uiImage: contactsManager.textToImage(
firstName: historyListViewModel.callLogs[index].toAddress!.displayName!,
lastName: historyListViewModel.callLogs[index].toAddress!.displayName!.components(separatedBy: " ").count > 1
? historyListViewModel.callLogs[index].toAddress!.displayName!.components(separatedBy: " ")[1]
: ""))
.resizable()
.frame(width: 45, height: 45)
.clipShape(Circle())
} else {
Image(uiImage: contactsManager.textToImage(
firstName: historyListViewModel.callLogs[index].toAddress!.username ?? "Username Error",
lastName: historyListViewModel.callLogs[index].toAddress!.username!.components(separatedBy: " ").count > 1
? historyListViewModel.callLogs[index].toAddress!.username!.components(separatedBy: " ")[1]
firstName: callViewModel.calls[index].callLog!.remoteAddress!.username ?? "Username Error",
lastName: callViewModel.calls[index].callLog!.remoteAddress!.username!.components(separatedBy: " ").count > 1
? callViewModel.calls[index].callLog!.remoteAddress!.username!.components(separatedBy: " ")[1]
: ""))
.resizable()
.frame(width: 45, height: 45)
.clipShape(Circle())
}
} else if historyListViewModel.callLogs[index].fromAddress != nil {
if historyListViewModel.callLogs[index].fromAddress!.displayName != nil {
Image(uiImage: contactsManager.textToImage(
firstName: historyListViewModel.callLogs[index].fromAddress!.displayName!,
lastName: historyListViewModel.callLogs[index].fromAddress!.displayName!.components(separatedBy: " ").count > 1
? historyListViewModel.callLogs[index].fromAddress!.displayName!.components(separatedBy: " ")[1]
: ""))
.resizable()
.frame(width: 45, height: 45)
.clipShape(Circle())
} else {
Image(uiImage: contactsManager.textToImage(
firstName: historyListViewModel.callLogs[index].fromAddress!.username ?? "Username Error",
lastName: historyListViewModel.callLogs[index].fromAddress!.username!.components(separatedBy: " ").count > 1
? historyListViewModel.callLogs[index].fromAddress!.username!.components(separatedBy: " ")[1]
: ""))
.resizable()
.frame(width: 45, height: 45)
.clipShape(Circle())
}
} else {
Image("profil-picture-default")
.resizable()
.frame(width: 45, height: 45)
.clipShape(Circle())
}
}
}
VStack(spacing: 0) {
Spacer()
let fromAddressFriend = contactsManager.getFriendWithAddress(address: historyListViewModel.callLogs[index].fromAddress!)
let toAddressFriend = contactsManager.getFriendWithAddress(address: historyListViewModel.callLogs[index].toAddress!)
let addressFriend = historyListViewModel.callLogs[index].dir == .Incoming ? fromAddressFriend : toAddressFriend
if addressFriend != nil {
Text(addressFriend!.name!)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
.default_text_style(styleSize: 16)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
} else {
if historyListViewModel.callLogs[index].dir == .Outgoing && historyListViewModel.callLogs[index].toAddress != nil {
Text(historyListViewModel.callLogs[index].toAddress!.displayName != nil
? historyListViewModel.callLogs[index].toAddress!.displayName!
: historyListViewModel.callLogs[index].toAddress!.username!)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
} else if historyListViewModel.callLogs[index].fromAddress != nil {
Text(historyListViewModel.callLogs[index].fromAddress!.displayName != nil
? historyListViewModel.callLogs[index].fromAddress!.displayName!
: historyListViewModel.callLogs[index].fromAddress!.username!)
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
}
}
HStack {
Image(historyListViewModel.getCallIconResId(callStatus: historyListViewModel.callLogs[index].status, callDir: historyListViewModel.callLogs[index].dir))
.resizable()
.frame(
width: historyListViewModel.getCallIconResId(callStatus: historyListViewModel.callLogs[index].status, callDir: historyListViewModel.callLogs[index].dir).contains("rejected") ? 12 : 8,
height: historyListViewModel.getCallIconResId(callStatus: historyListViewModel.callLogs[index].status, callDir: historyListViewModel.callLogs[index].dir).contains("rejected") ? 6 : 8)
Text(historyListViewModel.getCallTime(startDate: historyListViewModel.callLogs[index].startDate))
.default_text_style_300(styleSize: 12)
.frame(maxWidth: .infinity, alignment: .leading)
Spacer()
Text(callViewModel.calls[index].callLog!.remoteAddress!.displayName != nil
? callViewModel.calls[index].callLog!.remoteAddress!.displayName!
: callViewModel.calls[index].callLog!.remoteAddress!.username!)
.default_text_style(styleSize: 16)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
}
Spacer()
HStack {
if callViewModel.calls[index].state == .PausedByRemote
|| callViewModel.calls[index].state == .Pausing
|| callViewModel.calls[index].state == .Paused
|| callViewModel.calls[index].state == .Resuming {
Text(callViewModel.calls[index].state == .Resuming ? "Resuming" : "Paused")
.default_text_style_300(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .trailing)
.lineLimit(1)
.padding(.horizontal, 4)
Image("pause")
.resizable()
.frame(width: 25, height: 25)
} else {
Text("Active")
.default_text_style_300(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .trailing)
.lineLimit(1)
.padding(.horizontal, 4)
Image("phone-call")
.resizable()
.frame(width: 25, height: 25)
}
}
}
Image("phone")
.resizable()
.frame(width: 25, height: 25)
.padding(.all, 10)
.padding(.trailing, 5)
.highPriorityGesture(
TapGesture()
.onEnded { _ in
withAnimation {
if historyListViewModel.callLogs[index].dir == .Outgoing && historyListViewModel.callLogs[index].toAddress != nil {
telecomManager.doCallWithCore(
addr: historyListViewModel.callLogs[index].toAddress!, isVideo: false
)
} else if historyListViewModel.callLogs[index].fromAddress != nil {
telecomManager.doCallWithCore(
addr: historyListViewModel.callLogs[index].fromAddress!, isVideo: false
)
}
historyViewModel.displayedCall = nil
}
}
)
}
}
.buttonStyle(.borderless)
.listRowInsets(EdgeInsets(top: 5, leading: 20, bottom: 5, trailing: 20))
.listRowInsets(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20))
.listRowSeparator(.hidden)
.background(.white)
.onTapGesture {
withAnimation {
historyViewModel.displayedCall = historyListViewModel.callLogs[index]
if callViewModel.currentCall != nil && callViewModel.calls[index].callLog!.callId == callViewModel.currentCall!.callLog!.callId {
if callViewModel.currentCall!.state == .StreamsRunning {
do {
try callViewModel.currentCall!.pause()
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
callViewModel.isPaused = true
}
} catch {
}
} else {
do {
try callViewModel.currentCall!.resume()
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
callViewModel.isPaused = false
}
} catch {
}
}
} else {
TelecomManager.shared.setHeldOtherCallsWithCore(exceptCallid: "")
TelecomManager.shared.setHeld(call: callViewModel.calls[index], hold: callViewModel.calls[index].state == .StreamsRunning)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
callViewModel.resetCallView()
}
}
}
.onLongPressGesture(minimumDuration: 0.2) {
historyViewModel.selectedCall = historyListViewModel.callLogs[index]
showingSheet.toggle()
}
}
}
.listStyle(.plain)
.overlay(
VStack {
if historyListViewModel.callLogs.isEmpty {
if callViewModel.calls.isEmpty {
Spacer()
Image("illus-belledonne")
.resizable()
@ -271,9 +245,8 @@ struct CallsListFragment: View {
.navigationTitle("")
.navigationBarHidden(true)
}
*/
}
#Preview {
CallsListFragment(callsListViewModel: CallsListViewModel(), isShowCallsListFragment: .constant(true))
CallsListFragment(callViewModel: CallViewModel(), isShowCallsListFragment: .constant(true))
}

View file

@ -44,6 +44,8 @@ class CallViewModel: ObservableObject {
@Published var isZrtpPq: Bool = false
@Published var isRemoteDeviceTrusted: Bool = false
var calls: [Call] = []
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var currentCall: Call?
@ -113,6 +115,12 @@ class CallViewModel: ObservableObject {
}
}
func getCallsList() {
coreContext.doOnCoreQueue { core in
self.calls = core.calls
}
}
func terminateCall() {
coreContext.doOnCoreQueue { core in
if self.currentCall != nil {

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-iphone
*
* 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 <http://www.gnu.org/licenses/>.
*/
import Foundation
class CallsListViewModel: ObservableObject {
var coreContext = CoreContext.shared
//let nbCalls : Int
init() {
//self.getCallsList()
}
func getCallsList() {
coreContext.doOnCoreQueue { core in
core.callsNb
}
}
}