Skip to content

Commit

Permalink
Release/3.11.0 (#68)
Browse files Browse the repository at this point in the history
* 3.11.0 release

* 3.11.0 release

* updated pod file

* changed platform :ios, '13.0'
  • Loading branch information
rsarika authored Feb 13, 2024
1 parent 13c51e8 commit 582013f
Show file tree
Hide file tree
Showing 69 changed files with 5,643 additions and 349 deletions.
322 changes: 289 additions & 33 deletions KitchenSink.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

14 changes: 9 additions & 5 deletions KitchenSink/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

func navigateToLoginViewController() {
if !(UIApplication.shared.topViewController() is CallViewController) {
window?.rootViewController = LoginViewController()
if #available(iOS 16.0, *) {
window?.rootViewController = WelcomeViewController()
} else {
window?.rootViewController = LoginViewController()
}
}
}

Expand Down Expand Up @@ -234,10 +238,10 @@ extension AppDelegate: PKPushRegistryDelegate {
return
}

guard let authType = UserDefaults.standard.string(forKey: "loginType") else { return }
if authType == "jwt" {
guard let authType = UserDefaults.standard.string(forKey: Constants.loginTypeKey) else { return }
if authType == Constants.loginTypeValue.jwt.rawValue {
initWebexUsingJWT()
} else if authType == "token" {
} else if authType == Constants.loginTypeValue.token.rawValue{
initWebexUsingToken()
} else {
initWebexUsingOauth()
Expand Down Expand Up @@ -266,7 +270,7 @@ extension AppDelegate: PKPushRegistryDelegate {
let scopes = "spark:all" // spark:all is always mandatory

// See if we already have an email stored in UserDefaults else get it from user and do new Login
if let email = EmailAddress.fromString(UserDefaults.standard.value(forKey: "userEmail") as? String) {
if let email = EmailAddress.fromString(UserDefaults.standard.value(forKey: Constants.emailKey) as? String) {
// The scope parameter can be a space separated list of scopes that you want your access token to possess
let authenticator = OAuthAuthenticator(clientId: clientId, clientSecret: clientSecret, scope: scopes, redirectUri: redirectUri, emailId: email.toString())
webex = Webex(authenticator: authenticator)
Expand Down
24 changes: 20 additions & 4 deletions KitchenSink/Controllers/CallViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1800,11 +1800,27 @@ class CallViewController: UIViewController, MultiStreamObserver, UICollectionVie
}
switch reason {
case .callEnded, .localLeft, .localDecline, .localCancel, .remoteLeft, .remoteDecline, .remoteCancel, .otherConnected, .otherDeclined:
print("Call Disconnected: \(reason)")
CallObjectStorage.self.shared.removeCallObject(callId: call.callId ?? "")
var shouldRemove = true
switch reason {
case .localLeft:
// Meetings should not stop if local left and other party is still in meeting
if call.isMeeting {
// TODO: Also need to confirm if selfUser is not the meeting host when we have an API for it
shouldRemove = false
}
default:
break
}
if shouldRemove {
incomingCallData = incomingCallData.filter { $0.meetingId != call.meetingId }
CallObjectStorage.self.shared.removeCallObject(callId: call.callId ?? "")
NotificationCenter.default.post(name: Notification.Name("IncomingCallListChanged"), object: nil, userInfo: ["ring": false])
}
DispatchQueue.main.async { [weak self] in
print("CallVC dismiss onDisconnected")
if CallObjectStorage.self.shared.getAllActiveCalls().count == 0 {
// Need to dismiss CallVC only if no active calls are present or if the currently dismissed active call was a meeting
call.isSpaceMeeting
if call.isScheduledMeeting || CallObjectStorage.self.shared.getAllActiveCalls().filter({ $0.callId != call.callId}).count == 0 {
print("CallVC dismiss onDisconnected")
self?.dismiss(animated: true)
}
}
Expand Down
38 changes: 20 additions & 18 deletions KitchenSink/Controllers/HomeViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@ import MessageUI
import UIKit
import WebexSDK

class HomeViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
// MARK: Properties
fileprivate enum Feedback: CaseIterable {
static let recipient = "[email protected]"

case reportBug, featureRequest

var title: String {
switch self {
case .reportBug: return "Bug Report"
case .featureRequest: return "Feature Request"
}
enum Feedback: CaseIterable {
static let recipient = "[email protected]"

case reportBug, featureRequest

var title: String {
switch self {
case .reportBug: return "Bug Report"
case .featureRequest: return "Feature Request"
}
}
}

class HomeViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
// MARK: Properties
weak var webexUCLoginDelegate = webex.ucLoginDelegate
private let kCellId: String = "FeatureCell"
private var isUCServicesStarted = false
Expand Down Expand Up @@ -45,9 +46,9 @@ class HomeViewController: UIViewController, UICollectionViewDataSource, UICollec
DispatchQueue.main.async { [weak self] in
guard let appDelegate = (UIApplication.shared.delegate as? AppDelegate) else { fatalError() }
self?.navigationController?.dismiss(animated: true)
UserDefaults.standard.removeObject(forKey: "loginType")
UserDefaults.standard.removeObject(forKey: "userEmail")
UserDefaults.standard.removeObject(forKey: "isFedRAMP")
UserDefaults.standard.removeObject(forKey: Constants.loginTypeKey)
UserDefaults.standard.removeObject(forKey: Constants.emailKey)
UserDefaults.standard.removeObject(forKey: Constants.fedRampKey)
appDelegate.navigateToLoginViewController()
}
})
Expand Down Expand Up @@ -128,7 +129,7 @@ class HomeViewController: UIViewController, UICollectionViewDataSource, UICollec
webex.people.getMe(completionHandler: { [weak self] in
switch $0 {
case .success(let user):
UserDefaults.standard.set(user.id, forKey: "selfId")
UserDefaults.standard.set(user.id, forKey: Constants.selfId)
self?.currentUserButton.setTitle(user.initials, for: .normal)
self?.currentUserButton.layer.borderWidth = 2
case .failure(let error):
Expand Down Expand Up @@ -521,7 +522,7 @@ extension HomeViewController {
guard let keys = NSDictionary(contentsOfFile: path) else { return }
guard let token = token, let voipToken = voipToken else { return }

if let urlString = keys["registerationUrl"] as? String {
if let urlString = keys["registrationUrl"] as? String {
guard let serviceUrl = URL(string: urlString) else { print("Invalid URL"); return }

let parameters: [String: Any] = [
Expand Down Expand Up @@ -601,7 +602,7 @@ extension HomeViewController { // waiting call related code
AppDelegate.shared.callKitManager?.updateCall(call: call)
return
}
print("onIncoming Call object : + \(call.callId ?? "") , correlationId : \(call.correlationId ?? "") , externalTrackingId: + \(call.externalTrackingId ?? "")")
print("onIncoming Call object : \(call.callId ?? ""), correlationId : \(call.correlationId ?? "") , externalTrackingId: \(call.externalTrackingId ?? "")")
CallObjectStorage.self.shared.addCallObject(call: call)
call.onScheduleChanged = { c in
self.getUpdatedSchedule(call: c)
Expand Down Expand Up @@ -632,6 +633,7 @@ extension HomeViewController { // waiting call related code

func getUpdatedSchedule(call: Call) {
guard let callSchedule = call.schedules else {
// Case: For instant meetings started in a space
// Case : One to one call ( Only utilizes the title, Space, callId and isScheduledCall)
let newCall = Meeting(organizer: call.title ?? "", start: Date(), end: Date(), meetingId: "", link: "", subject: "", isScheduledCall: false, space: Space(id: call.spaceId ?? "", title: call.title ?? ""), currentCallId: call.callId ?? "")
// Flag to check if meeting is already scheduled (To enter change in the schedule)
Expand Down
144 changes: 125 additions & 19 deletions KitchenSink/Controllers/InitiateCall/ParticipantListViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,62 @@ class ParticipantListViewController: UIViewController, UITableViewDataSource, UI
setupViews()
setupConstraints()
participantsStatesProcess()
if call.isGroupCall && !call.isCUCMCall {

guard !call.isCUCMCall else {return}
self.navigationItem.setLeftBarButton(inviteParticipant, animated: true)
if call.isGroupCall {
self.navigationItem.setRightBarButton(muteAllButton, animated: true)
}

}

@objc private func inviteParticipant(_ sender: UIButton) {
let alert = UIAlertController(title: "Invite Participant", message: "Enter email id or contact id of participant", preferredStyle: .alert)
alert.addTextField { (textField) in
textField.placeholder = "Email or contact id"
textField.text = ""
}
alert.addAction(UIAlertAction(title: "Add", style: .default, handler: { [weak self] _ in
guard let email = alert.textFields?.first?.text else { return }
self?.call.inviteParticipant(participant: email) { result in
var errorMessage = ""

switch result {
case .success():
errorMessage = "added successfully"
case .failure(let error):

switch error as! InviteParticipantError
{
case .InvalidContactIdOrEmail:
errorMessage = "Invalid ContactId Or Email"
break
case .AlreadyJoined:
errorMessage = "Already Joined"
break
case .AlreadyInvited:
errorMessage = "Already Invited"
break
case .NotAHostOrCoHost:
errorMessage = "Not A Host Or CoHost"
break
case .UnknownError:
errorMessage = "unknown error"
break
default:
errorMessage = "unknown error"
}
@unknown default:
break
}

let alert2 = UIAlertController(title: "Add guest Result", message: errorMessage, preferredStyle: .alert)
alert2.addAction(.dismissAction(withTitle: "OK"))
self?.present(alert2, animated: true)
}
}))
alert.addAction(.dismissAction(withTitle: "Cancel"))
self.present(alert, animated: true)
}

// MARK: TableView Datasource
Expand Down Expand Up @@ -81,7 +134,7 @@ class ParticipantListViewController: UIViewController, UITableViewDataSource, UI
default:
participant = inMeeting[indexPath.row]
}
cell.setupCell(name: "\(participant.displayName ?? "ParticipantX"): \(participant.deviceType ?? .unknown)" , isAudioMuted: !participant.sendingAudio)
cell.setupCell(name: "\(participant.displayName ?? "ParticipantX"): \(participant.deviceType ?? .unknown)" , isAudioMuted: !participant.sendingAudio, isHost: participant.isHost, isCoHost: participant.isCohost, isPresenter: participant.isPresenter)
return cell
}

Expand Down Expand Up @@ -117,27 +170,72 @@ class ParticipantListViewController: UIViewController, UITableViewDataSource, UI
}

func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
if indexPath.section == 1 {
if indexPath.section == 1 || indexPath.section == 0 {
return true
} else {
return false
}
}

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let contextItem = UIContextualAction(style: .normal, title: "Let In") { _, _, _ in
var callMembershipsToLetIn: [CallMembership]
callMembershipsToLetIn = [self.inLobby[indexPath.row]]
self.call.letIn(callMembershipsToLetIn, completionHandler: { error in
if error != nil {
print(error.debugDescription)

if indexPath.section == 1 {
let contextItem = UIContextualAction(style: .normal, title: "Let In") { _, _, _ in
var callMembershipsToLetIn: [CallMembership]
callMembershipsToLetIn = [self.inLobby[indexPath.row]]
self.call.letIn(callMembershipsToLetIn, completionHandler: { error in
if error != nil {
print(error.debugDescription)
}
})
}
contextItem.backgroundColor = .momentumGreen50
let swipeActions = UISwipeActionsConfiguration(actions: [contextItem])

return swipeActions
} else if indexPath.section == 0 {
var contextItem = UIContextualAction(style: .normal, title: "Make Host") { _, _, _ in
self.call.makeHost(participantId: self.inMeeting[indexPath.row].personId ?? "") { result in
switch result {
case .success():
self.slideInStateView(slideInMsg: "makeHost success")
case .failure(let error):
self.slideInStateView(slideInMsg: "makeHost failed \(error)")
}
}
})
}

if self.inMeeting[indexPath.row].isSelf { // for self user we show reclaim host
contextItem = UIContextualAction(style: .normal, title: "Reclaim Host") { _, _, _ in
let alert = UIAlertController(title: "Reclaim Host", message: "Enter Host Key", preferredStyle: .alert)

alert.addTextField { textField in
textField.placeholder = "Enter Host Key"
textField.text = ""
}
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak alert] _ in
let textField = alert?.textFields![0] // Force unwrapping because we know it exists.
self.call.reclaimHost(hostKey: textField?.text ?? "") { result in
switch result {
case .success():
self.slideInStateView(slideInMsg: "Reclaim Host success")
case .failure(let error):
self.slideInStateView(slideInMsg: "Reclaim Host failed \(error)")
}
}
}))
DispatchQueue.main.async {
self.present(alert, animated: true, completion: nil)
}
}
}
contextItem.backgroundColor = .momentumGreen50

let swipeActions = UISwipeActionsConfiguration(actions: [contextItem])

return swipeActions
}
contextItem.backgroundColor = .momentumGreen50
let swipeActions = UISwipeActionsConfiguration(actions: [contextItem])

return swipeActions
return nil
}

func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
Expand Down Expand Up @@ -180,6 +278,12 @@ class ParticipantListViewController: UIViewController, UITableViewDataSource, UI
return button
}()

// lazy var for inviteParticipant button
private lazy var inviteParticipant: UIBarButtonItem = {
let button = UIBarButtonItem(title: "Invite Participant", style: .done, target: self, action: #selector(inviteParticipant(_:)))
return button
}()

private lazy var tableView: UITableView = {
let table = UITableView(frame: .zero)
table.dataSource = self
Expand All @@ -201,11 +305,13 @@ class ParticipantListViewController: UIViewController, UITableViewDataSource, UI
}

private func slideInStateView(slideInMsg: String) {
let alert = UIAlertController(title: nil, message: slideInMsg, preferredStyle: .alert)
self.present(alert, animated: true)
let duration: Double = 2
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + duration) {
alert.dismiss(animated: true)
DispatchQueue.main.async {
let alert = UIAlertController(title: nil, message: slideInMsg, preferredStyle: .alert)
self.present(alert, animated: true)
let duration: Double = 2
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + duration) {
alert.dismiss(animated: true)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class CallingSpacesListViewController: UIViewController, UITableViewDataSource {
presence = presenceForSpaceId
}

cell.setupCell(name: space.title ?? "", presence: presence, buttonActionHandler: { [weak self] in self?.callSpace(space) })
cell.setupCell(name: space.title ?? "", presence: presence, isGroupSpace: (space.type == .group), buttonActionHandler: { [weak self] in self?.callSpace(space) })
return cell
}

Expand Down Expand Up @@ -116,7 +116,7 @@ extension CallingSpacesListViewController: NavigationItemSetupProtocol {

extension CallingSpacesListViewController {
func getPresenceStatus() {
let selfId = UserDefaults.standard.string(forKey: "selfId")
let selfId = UserDefaults.standard.string(forKey: Constants.selfId)
for spaceId in directSpaceIds {
webex.memberships.list(spaceId: spaceId, completionHandler: { result in
switch result {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class CallingSpacesTableViewCell: UITableViewCell, ReusableCell {
callButtonHandler = nil
}

func setupCell(name: String?, presence: Presence?, buttonActionHandler: @escaping ButtonActionHandler = {}) {
func setupCell(name: String?, presence: Presence?, isGroupSpace: Bool, buttonActionHandler: @escaping ButtonActionHandler = {}) {
if let presence = presence {
displayStatus(presence: presence)
} else {
Expand All @@ -75,6 +75,7 @@ class CallingSpacesTableViewCell: UITableViewCell, ReusableCell {
statusIcon.image = UIImage(named: "unknown")
}
titleLabel.text = name
callButton.isHidden = isGroupSpace
self.callButtonHandler = buttonActionHandler
}

Expand Down
Loading

0 comments on commit 582013f

Please sign in to comment.