alqindiirsyam 2 lat temu
rodzic
commit
d9dacc1615
20 zmienionych plików z 471 dodań i 220 usunięć
  1. 22 22
      appbuilder-ios/AppBuilder/AppBuilder.xcodeproj/project.pbxproj
  2. 6 6
      appbuilder-ios/AppBuilder/AppBuilder/FourthTabViewController.swift
  3. 3 3
      appbuilder-ios/NexilisLite/NexilisLite.podspec
  4. 57 7
      appbuilder-ios/NexilisLite/NexilisLite/Source/APIS.swift
  5. 41 1
      appbuilder-ios/NexilisLite/NexilisLite/Source/CoreMessage_TMessageBank.swift
  6. 1 0
      appbuilder-ios/NexilisLite/NexilisLite/Source/CoreMessage_TMessageCode.swift
  7. 75 2
      appbuilder-ios/NexilisLite/NexilisLite/Source/IncomingThread.swift
  8. 156 116
      appbuilder-ios/NexilisLite/NexilisLite/Source/Nexilis.swift
  9. 4 3
      appbuilder-ios/NexilisLite/NexilisLite/Source/TMessage.swift
  10. 0 5
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Call/QmeraAudioConference.swift
  11. 47 18
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Call/QmeraAudioViewController.swift
  12. 20 18
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Call/QmeraVideoViewController.swift
  13. 5 2
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift
  14. 1 1
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/ChangeDeviceViewController.swift
  15. 1 1
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/ChangeNamePassswordViewController.swift
  16. 2 2
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/ChangePasswordViewController.swift
  17. 19 2
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/HistoryCCViewController.swift
  18. 4 4
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/ProfileViewController.swift
  19. 6 6
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/SettingTableViewController.swift
  20. 1 1
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/SignUpSignIn.swift

+ 22 - 22
appbuilder-ios/AppBuilder/AppBuilder.xcodeproj/project.pbxproj

@@ -14,7 +14,7 @@
 		2401CEA1275490DB00B323BB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2401CE9F275490DB00B323BB /* Main.storyboard */; };
 		2401CEA3275490E600B323BB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2401CEA2275490E600B323BB /* Assets.xcassets */; };
 		2401CEA6275490E600B323BB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2401CEA4275490E600B323BB /* LaunchScreen.storyboard */; };
-		64C73E0D9E9CD36FD373F9D8 /* Pods_AppBuilder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F706066070D0174AE105C209 /* Pods_AppBuilder.framework */; };
+		8B68E76E8EC79CD0386179F7 /* Pods_AppBuilder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 286359D832B5EEB59F835E69 /* Pods_AppBuilder.framework */; };
 		A413B18727EACB20006D16EB /* PrefsUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = A413B18627EACB20006D16EB /* PrefsUtil.swift */; };
 		A42ED92227F30BA200B0FAB7 /* FirstTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A42ED92127F30BA200B0FAB7 /* FirstTabViewController.swift */; };
 		A42ED92427F3FC2F00B0FAB7 /* SecondTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A42ED92327F3FC2F00B0FAB7 /* SecondTabViewController.swift */; };
@@ -74,7 +74,9 @@
 		2401CEA2275490E600B323BB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		2401CEA5275490E600B323BB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		2401CEA7275490E600B323BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		65C757AC97A834D1917560B2 /* Pods-AppBuilder.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppBuilder.debug.xcconfig"; path = "Target Support Files/Pods-AppBuilder/Pods-AppBuilder.debug.xcconfig"; sourceTree = "<group>"; };
+		286359D832B5EEB59F835E69 /* Pods_AppBuilder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppBuilder.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		86F4F813DA81C40376134BE8 /* Pods-AppBuilder.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppBuilder.debug.xcconfig"; path = "Target Support Files/Pods-AppBuilder/Pods-AppBuilder.debug.xcconfig"; sourceTree = "<group>"; };
+		9CBF52872588FEC0649D8708 /* Pods-AppBuilder.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppBuilder.release.xcconfig"; path = "Target Support Files/Pods-AppBuilder/Pods-AppBuilder.release.xcconfig"; sourceTree = "<group>"; };
 		A413B18627EACB20006D16EB /* PrefsUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsUtil.swift; sourceTree = "<group>"; };
 		A42ED92127F30BA200B0FAB7 /* FirstTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstTabViewController.swift; sourceTree = "<group>"; };
 		A42ED92327F3FC2F00B0FAB7 /* SecondTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondTabViewController.swift; sourceTree = "<group>"; };
@@ -86,11 +88,9 @@
 		A4B5F03B27F193DC0089B871 /* Poppins-LightItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-LightItalic.ttf"; sourceTree = "<group>"; };
 		A4B5F03C27F193DC0089B871 /* Poppins-SemiBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-SemiBold.ttf"; sourceTree = "<group>"; };
 		A4B5F03D27F193DC0089B871 /* Poppins-MediumItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-MediumItalic.ttf"; sourceTree = "<group>"; };
-		A8A4F2997036256E6F370DE3 /* Pods-AppBuilder.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppBuilder.release.xcconfig"; path = "Target Support Files/Pods-AppBuilder/Pods-AppBuilder.release.xcconfig"; sourceTree = "<group>"; };
 		CDEE3DC929B06E1E00B420E5 /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
 		CDEE3DCB29B06E1E00B420E5 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
 		CDEE3DCD29B06E1E00B420E5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		F706066070D0174AE105C209 /* Pods_AppBuilder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppBuilder.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -98,7 +98,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				64C73E0D9E9CD36FD373F9D8 /* Pods_AppBuilder.framework in Frameworks */,
+				8B68E76E8EC79CD0386179F7 /* Pods_AppBuilder.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -119,7 +119,7 @@
 				CDEE3DCA29B06E1E00B420E5 /* NotificationService */,
 				2401CE97275490DB00B323BB /* Products */,
 				6E32BCCF4DE50EE1A90E8AAE /* Pods */,
-				EB9643D0C6F881AD1B5AFD88 /* Frameworks */,
+				2F89B8A584A02CBC3C334BD2 /* Frameworks */,
 			);
 			sourceTree = "<group>";
 		};
@@ -152,11 +152,19 @@
 			path = AppBuilder;
 			sourceTree = "<group>";
 		};
+		2F89B8A584A02CBC3C334BD2 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				286359D832B5EEB59F835E69 /* Pods_AppBuilder.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
 		6E32BCCF4DE50EE1A90E8AAE /* Pods */ = {
 			isa = PBXGroup;
 			children = (
-				65C757AC97A834D1917560B2 /* Pods-AppBuilder.debug.xcconfig */,
-				A8A4F2997036256E6F370DE3 /* Pods-AppBuilder.release.xcconfig */,
+				86F4F813DA81C40376134BE8 /* Pods-AppBuilder.debug.xcconfig */,
+				9CBF52872588FEC0649D8708 /* Pods-AppBuilder.release.xcconfig */,
 			);
 			path = Pods;
 			sourceTree = "<group>";
@@ -184,14 +192,6 @@
 			path = NotificationService;
 			sourceTree = "<group>";
 		};
-		EB9643D0C6F881AD1B5AFD88 /* Frameworks */ = {
-			isa = PBXGroup;
-			children = (
-				F706066070D0174AE105C209 /* Pods_AppBuilder.framework */,
-			);
-			name = Frameworks;
-			sourceTree = "<group>";
-		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
@@ -199,13 +199,13 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 2401CEC0275490E600B323BB /* Build configuration list for PBXNativeTarget "AppBuilder" */;
 			buildPhases = (
-				81EC0B8B73DF791FC5DE2676 /* [CP] Check Pods Manifest.lock */,
+				5C3F5CA54D12D5CEEA326725 /* [CP] Check Pods Manifest.lock */,
 				2401CE92275490DB00B323BB /* Sources */,
 				2401CE93275490DB00B323BB /* Frameworks */,
 				2401CE94275490DB00B323BB /* Resources */,
 				247E0A722796969200430E5F /* Embed Frameworks */,
 				CDEE3DD129B06E1E00B420E5 /* Embed Foundation Extensions */,
-				07199500606CD285677B26A8 /* [CP] Embed Pods Frameworks */,
+				3A1C735B8CDE2075245568C6 /* [CP] Embed Pods Frameworks */,
 			);
 			buildRules = (
 			);
@@ -298,7 +298,7 @@
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
-		07199500606CD285677B26A8 /* [CP] Embed Pods Frameworks */ = {
+		3A1C735B8CDE2075245568C6 /* [CP] Embed Pods Frameworks */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -315,7 +315,7 @@
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AppBuilder/Pods-AppBuilder-frameworks.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		81EC0B8B73DF791FC5DE2676 /* [CP] Check Pods Manifest.lock */ = {
+		5C3F5CA54D12D5CEEA326725 /* [CP] Check Pods Manifest.lock */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -511,7 +511,7 @@
 		};
 		2401CEC1275490E600B323BB /* Debug */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = 65C757AC97A834D1917560B2 /* Pods-AppBuilder.debug.xcconfig */;
+			baseConfigurationReference = 86F4F813DA81C40376134BE8 /* Pods-AppBuilder.debug.xcconfig */;
 			buildSettings = {
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
@@ -543,7 +543,7 @@
 		};
 		2401CEC2275490E600B323BB /* Release */ = {
 			isa = XCBuildConfiguration;
-			baseConfigurationReference = A8A4F2997036256E6F370DE3 /* Pods-AppBuilder.release.xcconfig */;
+			baseConfigurationReference = 9CBF52872588FEC0649D8708 /* Pods-AppBuilder.release.xcconfig */;
 			buildSettings = {
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;

+ 6 - 6
appbuilder-ios/AppBuilder/AppBuilder/FourthTabViewController.swift

@@ -864,7 +864,7 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
         DispatchQueue.global().async {
             let idMe = UserDefaults.standard.string(forKey: "me") as String?
             let p_password = password
-            let md5Hex = Utils.getMD5(string: p_password).map { String(format: "%02hhx", $0) }.joined()
+            let md5Hex = p_password
             var result: Bool = false
             if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getSignInApiAdmin(p_name: idMe!, p_password: md5Hex)) {
                 if response.isOk() {
@@ -899,7 +899,7 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
         DispatchQueue.global().async {
             let idMe = UserDefaults.standard.string(forKey: "me") as String?
             let p_password = password
-            let md5Hex = Utils.getMD5(string: p_password).map { String(format: "%02hhx", $0) }.joined()
+            let md5Hex = p_password
             var result: Bool = false
             if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getSignInApiInternal(p_name: idMe!, p_password: md5Hex)) {
                 if response.isOk() {
@@ -935,8 +935,8 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
             let idMe = UserDefaults.standard.string(forKey: "me") as String?
             let p_password = oldPassword
             let n_password = newPassword
-            let md5Hex = Utils.getMD5(string: p_password).map { String(format: "%02hhx", $0) }.joined()
-            let md5HexNew = Utils.getMD5(string: n_password).map { String(format: "%02hhx", $0) }.joined()
+            let md5Hex = p_password
+            let md5HexNew = n_password
             var result: Bool = false
             if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getChangePasswordAdmin(p_f_pin: idMe!, pwd_en: md5HexNew, pwd_old: md5Hex)) {
                 if response.isOk() {
@@ -972,8 +972,8 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
             let idMe = UserDefaults.standard.string(forKey: "me") as String?
             let p_password = oldPassword
             let n_password = newPassword
-            let md5Hex = Utils.getMD5(string: p_password).map { String(format: "%02hhx", $0) }.joined()
-            let md5HexNew = Utils.getMD5(string: n_password).map { String(format: "%02hhx", $0) }.joined()
+            let md5Hex = p_password
+            let md5HexNew = n_password
             var result: Bool = false
             if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getChangePasswordInternal(p_f_pin: idMe!, pwd_en: md5HexNew, pwd_old: md5Hex)) {
                 if response.isOk() {

+ 3 - 3
appbuilder-ios/NexilisLite/NexilisLite.podspec

@@ -8,7 +8,7 @@
 
 Pod::Spec.new do |spec|
   spec.name         = "NexilisLite"
-  spec.version      = "1.0.6"
+  spec.version      = "1.0.7"
   spec.summary      = "NexilisLite Framework"
   spec.description  = <<-DESC
   NexilisLite Framework, embed Contact Center, Live Streaming, Push Notifications, Instant Messaging, Video and VoIP Calling features into your mobile apps within minutes...
@@ -18,13 +18,13 @@ Pod::Spec.new do |spec|
   spec.license      = "MIT"
   spec.author       = { "Yayan D Wicaksono" => "ya2n.wicaksono@gmail.com" }
   spec.ios.deployment_target = "14.0"
-#  spec.source       = { :http => 'https://newuniverse.io/UCPaaSiOS/releases/download/NexilisLiteiOS/v1.0.6/NexilisLite.zip' }
+#  spec.source       = { :http => 'https://newuniverse.io/UCPaaSiOS/releases/download/NexilisLiteiOS/v1.0.7/NexilisLite.zip' }
   spec.source       = { :path => '.' }
   spec.source_files = 'NexilisLite/Source/**/*'
   spec.resource_bundles = { 'NexilisLite' => ['NexilisLite/Resource/**/*']}
   spec.swift_version = '5.5.1'
   spec.dependency 'FMDB', '~> 2.7.5'
-  spec.dependency 'nuSDKService', '~> 0.0.9'
+  spec.dependency 'nuSDKService', '~> 1.0.0'
   spec.dependency 'NotificationBannerSwift', '~> 3.2.1'
   spec.dependency 'Alamofire', '~> 5.7.0'
   spec.dependency 'SDWebImage', '~> 5.15.7'

+ 57 - 7
appbuilder-ios/NexilisLite/NexilisLite/Source/APIS.swift

@@ -61,14 +61,64 @@ public class APIS: NSObject {
             banner.show()
             return
         }
-        let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
-        controller.isContactCenter = true
-        let navigationController = UINavigationController(rootViewController: controller)
-        navigationController.defaultStyle()
-        if UIApplication.shared.visibleViewController?.navigationController != nil {
-            UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
+        if User.isCallCenter(userType: (User.getData(pin: User.getMyPin())?.userType)!) {
+            let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "myHistoryCC") as! HistoryCCViewController
+            controller.isOfficer = true
+            controller.fromAPI = true
+            let navigationController = UINavigationController(rootViewController: controller)
+            navigationController.defaultStyle()
+            if UIApplication.shared.visibleViewController?.navigationController != nil {
+                UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
+            } else {
+                UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
+            }
         } else {
-            UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
+            let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
+            controller.isContactCenter = true
+            let navigationController = UINavigationController(rootViewController: controller)
+            navigationController.defaultStyle()
+            if UIApplication.shared.visibleViewController?.navigationController != nil {
+                UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
+            } else {
+                UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
+            }
+        }
+    }
+    
+    public static func callContactCenter(mode: String, speciality: String) {
+        let isChangeProfile = Utils.getSetProfile()
+        if !isChangeProfile {
+            APIS.showChangeProfile()
+            return
+        }
+        let isWaitingRequestCC = UserDefaults.standard.bool(forKey: "waitingRequestCC")
+        if isWaitingRequestCC {
+            let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
+            imageView.tintColor = .white
+            let banner = FloatingNotificationBanner(title: "You have requested Call Center, please wait for response.".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .info, colors: nil, iconPosition: .center)
+            banner.show()
+            return
+        }
+        if User.isCallCenter(userType: (User.getData(pin: User.getMyPin())?.userType)!) {
+            let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "myHistoryCC") as! HistoryCCViewController
+            controller.isOfficer = true
+            let navigationController = UINavigationController(rootViewController: controller)
+            navigationController.defaultStyle()
+            if UIApplication.shared.visibleViewController?.navigationController != nil {
+                UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
+            } else {
+                UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
+            }
+        } else {
+            let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
+            controller.isContactCenter = true
+            let navigationController = UINavigationController(rootViewController: controller)
+            navigationController.defaultStyle()
+            if UIApplication.shared.visibleViewController?.navigationController != nil {
+                UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
+            } else {
+                UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
+            }
         }
     }
     

+ 41 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/CoreMessage_TMessageBank.swift

@@ -165,7 +165,11 @@ public class CoreMessage_TMessageBank {
         tmessage.mBodies[CoreMessage_TMessageKey.FORMAT] = ex_format
         tmessage.mBodies[CoreMessage_TMessageKey.IS_CALL_CENTER] = is_call_center
         tmessage.mBodies[CoreMessage_TMessageKey.CALL_CENTER_ID] = call_center_id
-        tmessage.mBodies[CoreMessage_TMessageKey.OPPOSITE_PIN] = opposite_pin
+        tmessage.mBodies[CoreMessage_TMessageKey.F_USER_ID] = me
+        tmessage.mBodies[CoreMessage_TMessageKey.QUANTITY] = "1"
+        if !opposite_pin.isEmpty {
+            tmessage.mBodies[CoreMessage_TMessageKey.OPPOSITE_PIN] = opposite_pin
+        }
         
         if !image_id.isEmpty {
             tmessage.mBodies[CoreMessage_TMessageKey.IMAGE_ID] = image_id
@@ -531,6 +535,15 @@ public class CoreMessage_TMessageBank {
         return tMessage
     }
     
+    public static func getAddFriendQRCodeSilent(fpin: String) -> TMessage {
+        let tMessage = TMessage()
+        tMessage.mCode = CoreMessage_TMessageCode.ADD_FRIEND_QR
+        tMessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tMessage.mBodies[CoreMessage_TMessageKey.FRIEND_FPIN] = fpin
+        tMessage.mBodies["is_silent"] = "1"
+        return tMessage
+    }
+    
     public static func removeFriend(lpin: String) -> TMessage {
         let tMessage = TMessage()
         tMessage.mCode = CoreMessage_TMessageCode.REMOVE_FRIEND
@@ -2354,4 +2367,31 @@ public class CoreMessage_TMessageBank {
         return tmessage
     }
     
+    public static func getServiceBank() -> TMessage {
+        let tmessage = TMessage()
+        tmessage.mCode = CoreMessage_TMessageCode.GET_SERVICE_BNI
+        tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tmessage.mPIN = UserDefaults.standard.string(forKey: "me")!
+        tmessage.mBodies[CoreMessage_TMessageKey.PLATFORM] = "0"
+        return tmessage
+    }
+    
+    public static func getIncomingCallCS(f_pin_opposite: String) -> TMessage {
+        let tmessage = TMessage()
+        tmessage.mCode = CoreMessage_TMessageCode.INCOMING_CALL_CC
+        tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tmessage.mPIN = UserDefaults.standard.string(forKey: "me")!
+        tmessage.mBodies[CoreMessage_TMessageKey.F_PIN] = f_pin_opposite
+        return tmessage
+    }
+
+    public static func getEndCall(f_pin_opposite: String) -> TMessage {
+        let tmessage = TMessage()
+        tmessage.mCode = CoreMessage_TMessageCode.END_CALL
+        tmessage.mStatus = CoreMessage_TMessageUtil.getTID()
+        tmessage.mPIN = UserDefaults.standard.string(forKey: "me")!
+        tmessage.mBodies[CoreMessage_TMessageKey.F_PIN] = f_pin_opposite
+        return tmessage;
+    }
+    
 }

+ 1 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/CoreMessage_TMessageCode.swift

@@ -584,6 +584,7 @@ public class CoreMessage_TMessageCode {
     public static let ASKING_FOR_END_CALL = "ASKE";
     
     public static let END_CALL = "ENCL";
+    public static let INCOMING_CALL_CC = "ICS";
     
     public static let POST_RETRIEVE_PROFILE = "PIP";
     public static let POST_RETRIEVE_TIMELINE = "PIT";

+ 75 - 2
appbuilder-ios/NexilisLite/NexilisLite/Source/IncomingThread.swift

@@ -54,10 +54,10 @@ class IncomingThread {
         } else if message.getCode() == CoreMessage_TMessageCode.PUSH_MYSELF || message.getCode() == CoreMessage_TMessageCode.PUSH_MYSELF_ACK || message.getCode() == CoreMessage_TMessageCode.PULL_MYSELF {
             pushMyself(message: message)
         } else if message.getCode() == CoreMessage_TMessageCode.PUSH_BUDDY {
-            makeNotifAddFriend(message: message)
             DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
                 self.initBatchBuddy(message: message)
             })
+            makeNotifAddFriend(message: message)
         } else if message.getCode() == CoreMessage_TMessageCode.INIT_BATCH_BUDDY {
             initBatchBuddy(message: message)
         } else if message.getCode() == CoreMessage_TMessageCode.CHANGE_BATCH_PERSON_INFO {
@@ -140,7 +140,7 @@ class IncomingThread {
             verifyOTP(message: message)
         } else if message.getCode() == CoreMessage_TMessageCode.REMOVE_FRIEND {
             deleteBuddy(message: message)
-        } else if message.getCode() == CoreMessage_TMessageCode.PUSH_CALL_CENTER || message.getCode() == CoreMessage_TMessageCode.ACCEPT_CALL_CENTER || message.getCode() == CoreMessage_TMessageCode.END_CALL_CENTER || message.getCode() == CoreMessage_TMessageCode.TIMEOUT_CONTACT_CENTER || message.getCode() == CoreMessage_TMessageCode.INVITE_TO_ROOM_CONTACT_CENTER || message.getCode() == CoreMessage_TMessageCode.ACCEPT_CONTACT_CENTER || message.getCode() == CoreMessage_TMessageCode.PUSH_MEMBER_ROOM_CONTACT_CENTER || message.getCode() == CoreMessage_TMessageCode.INVITE_END_CONTACT_CENTER || message.getCode() == CoreMessage_TMessageCode.INVITE_EXIT_CONTACT_CENTER {
+        } else if message.getCode() == CoreMessage_TMessageCode.PUSH_CALL_CENTER || message.getCode() == CoreMessage_TMessageCode.ACCEPT_CALL_CENTER || message.getCode() == CoreMessage_TMessageCode.END_CALL_CENTER || message.getCode() == CoreMessage_TMessageCode.TIMEOUT_CONTACT_CENTER || message.getCode() == CoreMessage_TMessageCode.INVITE_TO_ROOM_CONTACT_CENTER || message.getCode() == CoreMessage_TMessageCode.ACCEPT_CONTACT_CENTER || message.getCode() == CoreMessage_TMessageCode.PUSH_MEMBER_ROOM_CONTACT_CENTER || message.getCode() == CoreMessage_TMessageCode.INVITE_END_CONTACT_CENTER || message.getCode() == CoreMessage_TMessageCode.INVITE_EXIT_CONTACT_CENTER || message.getCode() == CoreMessage_TMessageCode.PUSH_SECOND_CONTACT_CENTER {
             handleCallCenter(message: message)
         } else if message.getCode() == CoreMessage_TMessageCode.PUSH_DISCUSSION_COMMENT {
             if let delegate = Nexilis.shared.messageDelegate {
@@ -161,6 +161,8 @@ class IncomingThread {
             backupAvailability(message: message)
         } else if message.getCode() == CoreMessage_TMessageCode.FORWARD_MESSAGE {
             incomingWBSS(message: message)
+        } else if message.getCode() == CoreMessage_TMessageCode.INCOMING_CALL_CC {
+            incomingCallCC(message: message)
         } else {
             print("unprocessed code", message.getCode())
             ack(message: message)
@@ -173,6 +175,71 @@ class IncomingThread {
     /**
      *
      */
+    private func incomingCallCC(message: TMessage) {
+        if let packetId = message.mBodies[CoreMessage_TMessageKey.PACKET_ID] {
+            _ = Nexilis.responseString(packetId: packetId, message: "01")
+        }
+        ack(message: message)
+        DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
+            let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
+            let channelCC = UserDefaults.standard.string(forKey: "channelCC") ?? ""
+            let complaintId = onGoingCC.components(separatedBy: ",")[2]
+            let fPinCC = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
+            if fPinCC == User.getMyPin() {
+                return
+            }
+            if channelCC == "1" {
+                let controller = QmeraAudioViewController()
+                controller.isOutgoing = false
+                controller.user = User.getData(pin: fPinCC)
+                controller.ticketId = complaintId
+                controller.modalPresentationStyle = .overCurrentContext
+                if UIApplication.shared.visibleViewController is UIAlertController {
+                    let vc = UIApplication.shared.visibleViewController as! UIAlertController
+                    vc.dismiss(animated: true, completion: {
+                        if UIApplication.shared.visibleViewController?.navigationController != nil {
+                            UIApplication.shared.visibleViewController?.navigationController?.present(controller, animated: true, completion: nil)
+                        } else {
+                            UIApplication.shared.visibleViewController?.present(controller, animated: true, completion: nil)
+                        }
+                    })
+                    return
+                }
+                if UIApplication.shared.visibleViewController?.navigationController != nil {
+                    UIApplication.shared.visibleViewController?.navigationController?.present(controller, animated: true, completion: nil)
+                } else {
+                    UIApplication.shared.visibleViewController?.present(controller, animated: true, completion: nil)
+                }
+            } else if channelCC == "2" {
+                let videoController = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "videoVCQmera") as! QmeraVideoViewController
+                let user = User.getData(pin: fPinCC)
+                videoController.fPin = user!.pin
+                videoController.isInisiator = false
+                videoController.users.append(user!)
+                videoController.isAutoAccept = true
+                videoController.ticketId = complaintId
+                let navigationController = UINavigationController(rootViewController: videoController)
+                navigationController.modalPresentationStyle = .fullScreen
+                if UIApplication.shared.visibleViewController is UIAlertController {
+                    let vc = UIApplication.shared.visibleViewController as! UIAlertController
+                    vc.dismiss(animated: true, completion: {
+                        if UIApplication.shared.visibleViewController?.navigationController != nil {
+                            UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
+                        } else {
+                            UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
+                        }
+                    })
+                    return
+                }
+                if UIApplication.shared.visibleViewController?.navigationController != nil {
+                    UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
+                } else {
+                    UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
+                }
+            }
+        })
+    }
+    
     private func incomingWBSS(message: TMessage) {
         let type = message.getBody(key: CoreMessage_TMessageKey.TYPE, default_value: "")
         let status = message.getBody(key: CoreMessage_TMessageKey.STATUS, default_value: "")
@@ -244,6 +311,9 @@ class IncomingThread {
                         if let cursorUser = Database.shared.getRecords(fmdb: fmdb, query: "SELECT f_pin FROM BUDDY where f_pin='\(f_pin)'"), cursorUser.next() {
                             return
                         }
+                        if message.getBody(key: "is_silent") == "1" {
+                            return
+                        }
                         let firstname = CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.FIRST_NAME)
                         let lastname = CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.LAST_NAME)
                         let f_pin = CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.F_PIN)
@@ -400,6 +470,9 @@ class IncomingThread {
         if let delegate = Nexilis.shared.messageDelegate {
             delegate.onReceive(message: message)
         }
+        if let packetId = message.mBodies[CoreMessage_TMessageKey.PACKET_ID], (message.getCode() == CoreMessage_TMessageCode.PUSH_CALL_CENTER || message.getCode() == CoreMessage_TMessageCode.PUSH_SECOND_CONTACT_CENTER) {
+            _ = Nexilis.responseString(packetId: packetId, message: "00")
+        }
         ack(message: message)
         // TODO: notif call center
     }

+ 156 - 116
appbuilder-ios/NexilisLite/NexilisLite/Source/Nexilis.swift

@@ -157,6 +157,8 @@ public class Nexilis: NSObject {
                         })
                     })
                 }
+                getServiceBank()
+                getPullWorkingArea()
                 delegate.onSuccess(userId: me)
                 if showButton {
                     DispatchQueue.main.async {
@@ -193,6 +195,44 @@ public class Nexilis: NSObject {
         }
     }
     
+    private static func getServiceBank() {
+        DispatchQueue.global().asyncAfter(deadline: .now(), execute: {
+            _ = Nexilis.write(message: CoreMessage_TMessageBank.getServiceBank())
+        })
+    }
+    
+    private static func getPullWorkingArea() {
+        DispatchQueue.global().asyncAfter(deadline: .now(), execute: {
+            if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getWorkingAreaContactCenter(), timeout: 30 * 1000), response.isOk() {
+                let data = response.getBody(key: CoreMessage_TMessageKey.DATA)
+                if !data.isEmpty {
+                    if let jsonArray = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
+                        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                            do {
+                                for json in jsonArray {
+                                    var parent = CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.PARENT_ID)
+                                    if parent.isEmpty {
+                                        parent = "-99"
+                                    }
+                                    _ = try Database.shared.insertRecord(fmdb: fmdb, table: "SERVICE_BANK", cvalues: [
+                                        "service_id" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.CATEGORY_ID),
+                                        "service_name" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.NAME),
+                                        "description" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.DESCRIPTION),
+                                        "parent" : parent,
+                                        "is_tablet" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.PLATFORM)
+                                    ], replace: true)
+                                }
+                            } catch {
+                                rollback.pointee = true
+                                print(error)
+                            }
+                        })
+                    }
+                }
+            }
+        })
+    }
+    
     public static func destroyAll() {
         let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
         if !onGoingCC.isEmpty {
@@ -263,7 +303,7 @@ public class Nexilis: NSObject {
             Nexilis.dispatch?.wait()
             Nexilis.dispatch = nil
             print("success change user to fpin")
-            _ = Nexilis.write(message: CoreMessage_TMessageBank.getChangeConnectionID(p_pin: f_pin))
+//            _ = Nexilis.write(message: CoreMessage_TMessageBank.getChangeConnectionID(p_pin: f_pin))
         } catch{
             print(error)
         }
@@ -422,6 +462,20 @@ public class Nexilis: NSObject {
         }
         return result
     }
+    
+    public static func responseString(packetId: String, message: String, timeout: Int = 15 * 1000) -> String? {
+        var result: String? = nil
+        do {
+            if !API.bInetConnAvailable() {
+                return nil
+            }
+            print(">> RESPONSE >> " + packetId + " " + message);
+            result = try API.sSendResponse(sRequestID: packetId, sResponse: message, lTimeout: timeout)
+        } catch {
+            print(error)
+        }
+        return result
+    }
 
     public static func setSpeaker(_ isEnabled: Bool) {
         do {
@@ -1390,6 +1444,9 @@ extension Nexilis: CallDelegate {
             let idMe = UserDefaults.standard.string(forKey: "me")!
             let myData = User.getData(pin: idMe)
             let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
+            if !onGoingCC.isEmpty {
+                return
+            }
             let deviceId = message.split(separator: ",")[0]
             if myData?.offline_mode == "1" || self.stateUnfriend == deviceId {
                 DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
@@ -1418,55 +1475,39 @@ extension Nexilis: CallDelegate {
                 }
             }
             if (state == 21 && message.split(separator: ",")[1] != "joining Ac.room on channel 0") {
-                if onGoingCC.isEmpty {
-                    let data = User.getDataCanNil(pin: String(deviceId))
-                    if data == nil {
-                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
-                            API.terminateCall(sParty: nil)
-                        })
-                        return
-                    }
+                let data = User.getDataCanNil(pin: String(deviceId))
+                if data == nil {
+                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
+                        API.terminateCall(sParty: nil)
+                    })
+                    return
+                }
 //                    let backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
 //                    uuidOngoing = UUID()
 //                    self.displayIncomingCall(uuid: uuidOngoing, handle: String(deviceId), hasVideo: false) { error in
 //                        UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier)
 //                    }
-                    let controller = QmeraAudioViewController()
-                    controller.user = User.getData(pin: String(deviceId))
-                    controller.isOutgoing = false
-                    controller.modalPresentationStyle = .overCurrentContext
-                    if UIApplication.shared.visibleViewController is UIAlertController {
-                        let vc = UIApplication.shared.visibleViewController as! UIAlertController
-                        vc.dismiss(animated: true, completion: {
-                            if UIApplication.shared.visibleViewController?.navigationController != nil {
-                                UIApplication.shared.visibleViewController?.navigationController?.present(controller, animated: true, completion: nil)
-                            } else {
-                                UIApplication.shared.visibleViewController?.present(controller, animated: true, completion: nil)
-                            }
-                        })
-                        return
-                    }
-                    if UIApplication.shared.visibleViewController?.navigationController != nil {
-                        UIApplication.shared.visibleViewController?.navigationController?.present(controller, animated: true, completion: nil)
-                    } else {
-                        UIApplication.shared.visibleViewController?.present(controller, animated: true, completion: nil)
-                    }
-//                    API.receiveCCall(sParty: String(deviceId))
-                } else {
-                    DispatchQueue.main.asyncAfter(deadline: .now() + isShowAlert!, execute: {
-                        let controller = QmeraAudioViewController()
-                        controller.user = User.getData(pin: String(deviceId))
-                        controller.isOnGoing = true
-                        controller.isOutgoing = false
-                        controller.modalPresentationStyle = .overCurrentContext
+                let controller = QmeraAudioViewController()
+                controller.user = User.getData(pin: String(deviceId))
+                controller.isOutgoing = false
+                controller.modalPresentationStyle = .overCurrentContext
+                if UIApplication.shared.visibleViewController is UIAlertController {
+                    let vc = UIApplication.shared.visibleViewController as! UIAlertController
+                    vc.dismiss(animated: true, completion: {
                         if UIApplication.shared.visibleViewController?.navigationController != nil {
                             UIApplication.shared.visibleViewController?.navigationController?.present(controller, animated: true, completion: nil)
                         } else {
                             UIApplication.shared.visibleViewController?.present(controller, animated: true, completion: nil)
                         }
-                        API.receiveCCall(sParty: String(deviceId))
                     })
+                    return
+                }
+                if UIApplication.shared.visibleViewController?.navigationController != nil {
+                    UIApplication.shared.visibleViewController?.navigationController?.present(controller, animated: true, completion: nil)
+                } else {
+                    UIApplication.shared.visibleViewController?.present(controller, animated: true, completion: nil)
                 }
+//                    API.receiveCCall(sParty: String(deviceId))
             } else if state == 31 {
                 let dataUser = User.getDataCanNil(pin: String(deviceId))
                 if dataUser == nil {
@@ -1475,74 +1516,27 @@ extension Nexilis: CallDelegate {
                     })
                     return
                 }
-                let fpin = deviceId
-                var data: [String: String?] = [:]
-                Database.shared.database?.inTransaction({ (fmdb, rollback) in
-                    if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: "SELECT f_pin, first_name, last_name, official_account, image_id, device_id, offline_mode, user_type FROM BUDDY where f_pin = '\(fpin)'"), cursorData.next() {
-                        data["f_pin"] = cursorData.string(forColumnIndex: 0)
-                        var name = ""
-                        if let firstname = cursorData.string(forColumnIndex: 1) {
-                            name = firstname
-                        }
-                        if let lastname = cursorData.string(forColumnIndex: 2) {
-                            name = name + " " + lastname
-                        }
-                        data["name"] = name
-                        data["picture"] = cursorData.string(forColumnIndex: 4)
-                        data["isOfficial"] = cursorData.string(forColumnIndex: 3)
-                        data["deviceId"] = cursorData.string(forColumnIndex: 5)
-                        data["isOffline"] = cursorData.string(forColumnIndex: 6)
-                        data["user_type"] = cursorData.string(forColumnIndex: 7)
-                        cursorData.close()
-                        
-                        let videoController = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "videoVCQmera") as! QmeraVideoViewController
-                        videoController.dataPerson.append(data)
-                        videoController.isInisiator = false
-                        if !onGoingCC.isEmpty {
-                            videoController.users.append(User.getData(pin: data["f_pin"]!!, fmdb: fmdb)!)
-                        }
-                        let navigationController = UINavigationController(rootViewController: videoController)
-                        navigationController.modalPresentationStyle = .fullScreen
-                        if !onGoingCC.isEmpty {
-                            videoController.isAutoAccept = true
-                            DispatchQueue.main.asyncAfter(deadline: .now() + isShowAlert!, execute: {
-                                if UIApplication.shared.visibleViewController is UIAlertController {
-                                    let vc = UIApplication.shared.visibleViewController as! UIAlertController
-                                    vc.dismiss(animated: true, completion: {
-                                        if UIApplication.shared.visibleViewController?.navigationController != nil {
-                                            UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
-                                        } else {
-                                            UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
-                                        }
-                                    })
-                                    return
-                                }
-                                if UIApplication.shared.visibleViewController?.navigationController != nil {
-                                    UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
-                                } else {
-                                    UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
-                                }
-                            })
+                let videoController = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "videoVCQmera") as! QmeraVideoViewController
+                videoController.fPin = String(deviceId)
+                videoController.isInisiator = false
+                let navigationController = UINavigationController(rootViewController: videoController)
+                navigationController.modalPresentationStyle = .fullScreen
+                if UIApplication.shared.visibleViewController is UIAlertController {
+                    let vc = UIApplication.shared.visibleViewController as! UIAlertController
+                    vc.dismiss(animated: true, completion: {
+                        if UIApplication.shared.visibleViewController?.navigationController != nil {
+                            UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
                         } else {
-                            if UIApplication.shared.visibleViewController is UIAlertController {
-                                let vc = UIApplication.shared.visibleViewController as! UIAlertController
-                                vc.dismiss(animated: true, completion: {
-                                    if UIApplication.shared.visibleViewController?.navigationController != nil {
-                                        UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
-                                    } else {
-                                        UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
-                                    }
-                                })
-                                return
-                            }
-                            if UIApplication.shared.visibleViewController?.navigationController != nil {
-                                UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
-                            } else {
-                                UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
-                            }
+                            UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
                         }
-                    }
-                })
+                    })
+                    return
+                }
+                if UIApplication.shared.visibleViewController?.navigationController != nil {
+                    UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
+                } else {
+                    UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
+                }
             }
         }
     }
@@ -1579,6 +1573,7 @@ extension Nexilis: CallDelegate {
 }
 
 var previewItem : NSURL?
+var listCCIdInv: [String] = []
 
 extension Nexilis: MessageDelegate {
     public func onReceiveComment(message: TMessage) {
@@ -1847,6 +1842,10 @@ extension Nexilis: MessageDelegate {
         dataMessage["message"] = message
         NotificationCenter.default.post(name: NSNotification.Name(rawValue: "onReceiveChat"), object: nil, userInfo: dataMessage)
         if message.getCode() == CoreMessage_TMessageCode.PUSH_CALL_CENTER {
+            if User.getDataCanNil(pin: message.getBody(key: CoreMessage_TMessageKey.L_PIN)) == nil {
+                Nexilis.addFriendSilent(fpin: message.getBody(key: CoreMessage_TMessageKey.L_PIN))
+                sleep(1)
+            }
             DispatchQueue.main.async {
                 if Nexilis.onGoingPushCC.isEmpty {
                     var data: [String: String] = [:]
@@ -1896,6 +1895,7 @@ extension Nexilis: MessageDelegate {
                         DispatchQueue.global().async {
                             DispatchQueue.global().async {
                                 _ = Nexilis.write(message: CoreMessage_TMessageBank.timeOutRequestCallCenter(channel: message.getBody(key: CoreMessage_TMessageKey.CHANNEL), l_pin: message.getBody(key: CoreMessage_TMessageKey.L_PIN)))
+                                
                             }
                         }
                         Nexilis.onGoingPushCC.removeAll()
@@ -2041,6 +2041,7 @@ extension Nexilis: MessageDelegate {
                                                 let controller = QmeraAudioViewController()
                                                 controller.user = User.getData(pin: pin)
                                                 controller.isOutgoing = true
+                                                controller.ticketId = complaintId
                                                 controller.modalPresentationStyle = .overCurrentContext
                                                 let navigationController = UINavigationController(rootViewController: controller)
                                                 navigationController.modalPresentationStyle = .fullScreen
@@ -2053,6 +2054,7 @@ extension Nexilis: MessageDelegate {
                                                 let videoVC = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "videoVCQmera") as! QmeraVideoViewController
                                                 videoVC.fPin = message.getBody(key: CoreMessage_TMessageKey.L_PIN)
                                                 videoVC.users.append(User.getData(pin: message.getBody(key: CoreMessage_TMessageKey.L_PIN))!)
+                                                videoVC.ticketId = complaintId
                                                 let navigationController = UINavigationController(rootViewController: videoVC)
                                                 navigationController.modalPresentationStyle = .fullScreen
                                                 if UIApplication.shared.visibleViewController?.navigationController != nil {
@@ -2209,6 +2211,10 @@ extension Nexilis: MessageDelegate {
                 UserDefaults.standard.set("\(fPinContacCenter)", forKey: "membersCC")
             }
         } else if message.getCode() == CoreMessage_TMessageCode.INVITE_TO_ROOM_CONTACT_CENTER {
+            if listCCIdInv.contains(message.getBody(key: CoreMessage_TMessageKey.CALL_CENTER_ID)) {
+                return
+            }
+            listCCIdInv.append(message.getBody(key: CoreMessage_TMessageKey.CALL_CENTER_ID))
             DispatchQueue.main.async {
                 let alert = UIAlertController(title: "", message: "\n\n\n\n\n\n\n\n\n\n".localized(), preferredStyle: .alert)
                 let newWidth = UIScreen.main.bounds.width * 0.90 - 270
@@ -2226,6 +2232,7 @@ extension Nexilis: MessageDelegate {
                 alert.view.subviews.first?.subviews.first?.subviews.first?.backgroundColor = .lightGray
                 alert.view.tintColor = .black
                 let rejectAction = UIAlertAction(title: "Reject".localized(), style: .destructive, handler: {(_) in
+                    listCCIdInv.removeAll(where: {$0 == message.getBody(key: CoreMessage_TMessageKey.CALL_CENTER_ID)})
                     DispatchQueue.global().async {
                         if let result = Nexilis.writeSync(message: CoreMessage_TMessageBank.acceptCCRoomInvite(l_pin: message.getPIN(), type: 0, ticket_id: message.getBody(key: CoreMessage_TMessageKey.CALL_CENTER_ID))) {
                             if result.isOk() {
@@ -2236,6 +2243,7 @@ extension Nexilis: MessageDelegate {
                     alert.dismiss(animated: true, completion: nil)
                 })
                 let acceptAction = UIAlertAction(title: "Accept".localized(), style: .default, handler: {(_) in
+                    listCCIdInv.removeAll(where: {$0 == message.getBody(key: CoreMessage_TMessageKey.CALL_CENTER_ID)})
                     let goAudioCall = Nexilis.checkMicPermission()
                     if !goAudioCall && message.getBody(key: CoreMessage_TMessageKey.CHANNEL) == "1" {
                         let alert = UIAlertController(title: "Attention!".localized(), message: "Please allow microphone permission in your settings".localized(), preferredStyle: .alert)
@@ -2384,19 +2392,12 @@ extension Nexilis: MessageDelegate {
                                                 if let userData = User.getData(pin: "\(json)") {
                                                     user.append(userData)
                                                 } else {
-                                                    Nexilis.addFriend (fpin: "\(json)") { result in
-                                                        DispatchQueue.main.async {
-                                                            if result {
-                                                                let userData = User.getData(pin: "\(json)")!
-                                                                user.append(userData)
-                                                            } else {
-                                                                let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                                                                imageView.tintColor = .white
-                                                                let banner = FloatingNotificationBanner(title: "Server busy, please try again later".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                                                                banner.show()
-                                                            }
+                                                    Nexilis.addFriendSilent(fpin: "\(json)")
+                                                    DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
+                                                        if let userData = User.getData(pin: "\(json)") {
+                                                            user.append(userData)
                                                         }
-                                                    }
+                                                    })
                                                 }
                                             }
                                         }
@@ -2429,6 +2430,39 @@ extension Nexilis: MessageDelegate {
                                             } else {
                                                 UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
                                             }
+                                        } else {
+                                            UserDefaults.standard.set("\(Date().currentTimeMillis())", forKey: "startTimeCC")
+                                            DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
+                                                if message.getBody(key: CoreMessage_TMessageKey.CHANNEL) == "1" {
+                                                    let pin = officer
+                                                    let controller = QmeraAudioViewController()
+                                                    controller.user = User.getData(pin: pin)
+                                                    controller.isOutgoing = false
+                                                    controller.ticketId = complaintId
+                                                    controller.modalPresentationStyle = .overCurrentContext
+                                                    let navigationController = UINavigationController(rootViewController: controller)
+                                                    navigationController.modalPresentationStyle = .fullScreen
+                                                    if UIApplication.shared.visibleViewController?.navigationController != nil {
+                                                        UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
+                                                    } else {
+                                                        UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
+                                                    }
+                                                } else if message.getBody(key: CoreMessage_TMessageKey.CHANNEL) == "2" {
+                                                    let videoVC = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "videoVCQmera") as! QmeraVideoViewController
+                                                    videoVC.fPin = officer
+                                                    videoVC.users.append(User.getData(pin: officer)!)
+                                                    videoVC.ticketId = complaintId
+                                                    videoVC.isInisiator = false
+                                                    videoVC.isAutoAccept = true
+                                                    let navigationController = UINavigationController(rootViewController: videoVC)
+                                                    navigationController.modalPresentationStyle = .fullScreen
+                                                    if UIApplication.shared.visibleViewController?.navigationController != nil {
+                                                        UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
+                                                    } else {
+                                                        UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
+                                                    }
+                                                }
+                                            })
                                         }
                                     }
                                 }
@@ -3060,6 +3094,12 @@ extension Nexilis: MessageDelegate {
         }
     }
     
+    public static func addFriendSilent(fpin: String) {
+        DispatchQueue.global().async {
+            _ = Nexilis.write(message: CoreMessage_TMessageBank.getAddFriendQRCodeSilent(fpin: fpin))
+        }
+    }
+    
     public func onReceive(message: [AnyHashable : Any?]) {
         var dataMessage: [AnyHashable : Any] = [:]
         dataMessage["message"] = message

+ 4 - 3
appbuilder-ios/NexilisLite/NexilisLite/Source/TMessage.swift

@@ -34,16 +34,17 @@ public class TMessage {
     public static let TYPE_NEED_ACK    =  "3"
     
     public init() {
+        mVersion = "1.0.107"
         mBodies[CoreMessage_TMessageKey.IMEI] = Nexilis.getCLMUserId()
 //        mBodies[CoreMessage_TMessageKey.VERCOD] = UIApplication.appVersion
-        mBodies[CoreMessage_TMessageKey.VERCOD] = "1.8.5.24"
+        mBodies[CoreMessage_TMessageKey.VERCOD] = "1.0.220216"
     }
     
     public init(data : String) {
         _ = unpack(data: data)
     }
     
-    init(type: String, version: String = "1.0.106", code: String,status: String, pin: String, l_pin: String, bodies:[String: String], media:  [UInt8]) {
+    init(type: String, version: String, code: String,status: String, pin: String, l_pin: String, bodies:[String: String], media:  [UInt8]) {
         mType = type
         mVersion = version
         mCode = code
@@ -54,7 +55,7 @@ public class TMessage {
         mMedia = media
         mBodies[CoreMessage_TMessageKey.IMEI] = Nexilis.getCLMUserId()
 //        mBodies[CoreMessage_TMessageKey.VERCOD] = UIApplication.appVersion
-        mBodies[CoreMessage_TMessageKey.VERCOD] = "1.8.5.24"
+        mBodies[CoreMessage_TMessageKey.VERCOD] = "1.0.220216"
     }
     
     public func clone(p_tmessage:TMessage) -> TMessage {

+ 0 - 5
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Call/QmeraAudioConference.swift

@@ -436,11 +436,6 @@ class QmeraAudioConference: UIViewController {
                         }
                     }
                     self.users.append(User.getData(pin: dataMessage.getPIN())!)
-                    // Start Calling
-                    if !self.isAddCall.isEmpty && self.isAddCall == dataMessage.getPIN(){
-//                        Nexilis.shared.callManager.startCall(handle: dataMessage.getPIN())
-                        API.initiateCCall(sParty: dataMessage.getPIN())
-                    }
                 }
             }
         }

+ 47 - 18
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Call/QmeraAudioViewController.swift

@@ -58,12 +58,25 @@ class QmeraAudioViewController: UIViewController {
     
     var isOnGoing: Bool = false
     
+    var ticketId: String = ""
+    
     private var timer: Timer?
     
     private var firstCall: Bool = true
     
     private var isSpeaker: Bool = false
     
+    var listRemoteViewFix: [UIImageView] = [
+        UIImageView(),
+        UIImageView(),
+        UIImageView(),
+        UIImageView(),
+        UIImageView()
+    ]
+    
+    let zoomView = UIImageView()
+    let cameraView = UIImageView()
+    
     let status: UILabel = {
         let label = UILabel()
         label.text = "Calling..."
@@ -236,29 +249,41 @@ class QmeraAudioViewController: UIViewController {
         name.anchor(top: profiles.bottomAnchor, left: view.leftAnchor, right: view.rightAnchor, paddingTop: 5, paddingLeft: 20, paddingRight: 20, centerX: view.centerXAnchor)
         definesPresentationContext = true
         
+        if isOutgoing {
+            outgoingView()
+        } else if isOnGoing {
+            ongoingView()
+        } else {
+            incomingView()
+        }
+        
+        UIDevice.current.isProximityMonitoringEnabled = true
+        
         NotificationCenter.default.addObserver(self, selector: #selector(onStatusCall(_:)), name: NSNotification.Name(rawValue: "onStatusCall"), object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(onReceiveMessage(notification:)), name: NSNotification.Name(rawValue: "onReceiveChat"), object: nil)
         
         if let u = self.user {
             self.users.append(u)
-            if isOutgoing {
+            if isOutgoing && ticketId.isEmpty {
 //                let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
 //                if onGoingCC.isEmpty {
 //                    Nexilis.shared.callManager.startCall(handle: u.pin)
 //                } else {
                     API.initiateCCall(sParty: u.pin)
 //                }
+            } else if !ticketId.isEmpty {
+                if isOutgoing {
+                    API.ccs(sTicketID: ticketId, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView, bCameraOn: false)
+                    if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getIncomingCallCS(f_pin_opposite: u.pin), timeout: 30 * 1000){
+                        if response.mBodies[CoreMessage_TMessageKey.ERRCOD] != "01" {
+                            self.didEnd(sender: true)
+                        }
+                    }
+                } else {
+                    API.csa(sTicketID: ticketId, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView, bCameraOn: false)
+                }
             }
         }
-        
-        if isOutgoing {
-            outgoingView()
-        } else if isOnGoing {
-            ongoingView()
-        } else {
-            incomingView()
-        }
-        UIDevice.current.isProximityMonitoringEnabled = true
     }
     
     override func viewWillLayoutSubviews() {
@@ -558,11 +583,6 @@ class QmeraAudioViewController: UIViewController {
                         }
                     }
                     self.users.append(User.getData(pin: dataMessage.getPIN())!)
-                    // Start Calling
-                    if !self.isAddCall.isEmpty && self.isAddCall == dataMessage.getPIN() {
-//                        Nexilis.shared.callManager.startCall(handle: dataMessage.getPIN())
-                        API.initiateCCall(sParty: dataMessage.getPIN())
-                    }
                 }
             }
         }
@@ -574,15 +594,24 @@ class QmeraAudioViewController: UIViewController {
            let message = data["message"] as? String
         {
             let arrayMessage = message.split(separator: ",")
-            if state == 23 {
+            if state == 23 || (!ticketId.isEmpty && state == 33) {
                 if users.count == 1 {
                     DispatchQueue.main.async {
                         self.status.text = "Ringing..."
                     }
                 }
-            } else if state == 22 {
+            } else if state == 22 || (!ticketId.isEmpty && state == 32) {
                 if users.count == 1 && firstCall {
                     DispatchQueue.main.async {
+                        if !self.ticketId.isEmpty {
+                            NSLayoutConstraint.deactivate(self.stack.constraints)
+                            self.stack.subviews.forEach { subview in
+                                subview.removeFromSuperview()
+                            }
+                            UIView.animate(withDuration: 0.3, animations: {
+                                self.view.layoutIfNeeded()
+                            })
+                        }
                         self.ongoingView()
                         let connectDate = Date()
                         self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
@@ -613,7 +642,7 @@ class QmeraAudioViewController: UIViewController {
                         }
                     }
                 }
-            } else if state == 28 {
+            } else if state == 28 || (!ticketId.isEmpty && state == 38) {
                 let onGoingCC = UserDefaults.standard.string(forKey: "onGoingCC") ?? ""
                 if let pin = arrayMessage.first, let index = users.firstIndex(of: User(pin: String(pin))) {
                     users.remove(at: index)

+ 20 - 18
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Call/QmeraVideoViewController.swift

@@ -64,6 +64,7 @@ class QmeraVideoViewController: UIViewController {
     var showNotifCCEnd = false
     var transformZoomAfterNewUserMore2 = false
     var isAddCall = ""
+    var ticketId = ""
     private var frontCamera = true
     var users: [User] = []
     let poweredByView: UIStackView = {
@@ -189,7 +190,9 @@ class QmeraVideoViewController: UIViewController {
                     row["deviceId"] = cursorData.string(forColumnIndex: 5)
                     row["isOffline"] = cursorData.string(forColumnIndex: 6)
                     row["user_type"] = cursorData.string(forColumnIndex: 7)
-                    dataPerson.append(row)
+                    if fPin != User.getMyPin() {
+                        dataPerson.append(row)
+                    }
                 } else {
                     var row: [String: String?] = [:]
                     row["f_pin"] = fPin
@@ -367,7 +370,16 @@ class QmeraVideoViewController: UIViewController {
         if isInisiator {
             labelIncomingOutgoing.text = "Outgoing video call".localized() + "..."
 //            Nexilis.startAudio()
-            API.initiateCCall(sParty: dataPerson[0]["f_pin"]!, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView)
+            if ticketId.isEmpty {
+                API.initiateCCall(sParty: dataPerson[0]["f_pin"]!, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView)
+            } else {
+                API.ccs(sTicketID: ticketId, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView, bCameraOn: true)
+                if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getIncomingCallCS(f_pin_opposite: users[0].pin), timeout: 30 * 1000){
+                    if response.mBodies[CoreMessage_TMessageKey.ERRCOD] != "01" {
+                        endAllCall()
+                    }
+                }
+            }
         } else {
             let systemSoundID: SystemSoundID = 1254
             AudioServicesPlaySystemSound(systemSoundID)
@@ -442,21 +454,6 @@ class QmeraVideoViewController: UIViewController {
                             UserDefaults.standard.set("\(members)", forKey: "membersCC")
                         }
                     }
-                    // Start Calling
-                    if !self.isAddCall.isEmpty && self.isAddCall == dataMessage.getPIN(){
-                        let user = User.getData(pin: dataMessage.getPIN())!
-                        var dataPerson: [String: String?] = [:]
-                        dataPerson["f_pin"] = user.pin
-                        dataPerson["name"] = user.fullName
-                        dataPerson["picture"] = user.thumb
-                        dataPerson["isOfficial"] = user.official
-                        dataPerson["deviceId"] = user.pin
-                        dataPerson["isOffline"] = ""
-                        dataPerson["user_type"] = user.userType
-                        self.dataPerson.append(dataPerson)
-                        self.users.append(user)
-                        API.initiateCCall(sParty: dataMessage.getPIN(), nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: self.listRemoteViewFix, ivLocalView: self.cameraView, ivRemoteZ: self.zoomView)
-                    }
                 }
             }
         }
@@ -539,7 +536,11 @@ class QmeraVideoViewController: UIViewController {
                 return
             }
 //            Nexilis.startAudio()
-            API.receiveCCall(sParty: dataPerson[0]["f_pin"]!, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView,ivRemoteZ: zoomView)
+            if ticketId.isEmpty {
+                API.receiveCCall(sParty: dataPerson[0]["f_pin"]!, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView,ivRemoteZ: zoomView)
+            } else {
+                API.csa(sTicketID: ticketId, nCamIdx: 1, nResIdx: 2, nVQuality: 4, ivRemoteView: listRemoteViewFix, ivLocalView: cameraView, ivRemoteZ: zoomView, bCameraOn: true)
+            }
         }
         DispatchQueue.main.async {
             self.myImage.removeFromSuperview()
@@ -793,6 +794,7 @@ class QmeraVideoViewController: UIViewController {
         cameraView.image = nil
         zoomView.image = nil
         listRemoteViewFix.removeAll()
+        dataPerson.removeAll()
     }
     
     func setSpeaker(isSpeaker: Bool) {

+ 5 - 2
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift

@@ -1308,6 +1308,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                     UserDefaults.standard.removeObject(forKey: "waitingRequestCC")
                     if dataMessage.getBody(key: CoreMessage_TMessageKey.CHANNEL) != "0" {
                         UserDefaults.standard.set("\(Date().currentTimeMillis())", forKey: "startTimeCC")
+                        UserDefaults.standard.set(dataMessage.getBody(key: CoreMessage_TMessageKey.CHANNEL), forKey: "channelCC")
                         DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
                             self.dismiss(animated: true, completion: nil)
                         })
@@ -2175,7 +2176,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         }
     }
     
-    private func sendChat(message_scope_id:String =  "3", status:String =  "2", message_text:String =  "", credential:String = "0", attachment_flag: String = "0", ex_blog_id: String = "", message_large_text: String = "", ex_format: String = "", image_id: String = "", audio_id: String = "", video_id: String = "", file_id: String = "", thumb_id: String = "", reff_id: String = "", read_receipts: String = "", chat_id: String = "", is_call_center: String = "0", call_center_id: String = "", viewController: UIViewController, isAutoSendCC : Bool = false) {
+    private func sendChat(message_scope_id:String =  "3", status:String =  "3", message_text:String =  "", credential:String = "0", attachment_flag: String = "0", ex_blog_id: String = "", message_large_text: String = "", ex_format: String = "", image_id: String = "", audio_id: String = "", video_id: String = "", file_id: String = "", thumb_id: String = "", reff_id: String = "", read_receipts: String = "4", chat_id: String = "", is_call_center: String = "0", call_center_id: String = "", viewController: UIViewController, isAutoSendCC : Bool = false) {
         if viewController is EditorPersonal && file_id == "" && dataMessageForward == nil && !isAutoSendCC{
             if ((textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) == "Send message".localized() && textFieldSend.textColor == UIColor.lightGray && attachment_flag != "11") || textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ) {
                 dismissKeyboard()
@@ -2197,6 +2198,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         var call_center_id = call_center_id
         var l_pin = dataPerson["f_pin"]!!
         var message_scope_id = message_scope_id
+        var chat_id = chat_id
         
         if (isContactCenter) {
             if fPinContacCenter.isEmpty && isRequestContactCenter {
@@ -2214,6 +2216,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
             call_center_id = complaintId
             l_pin = fPinContacCenter
             message_scope_id = "5"
+            chat_id = complaintId
             if isAutoSendCC {
                 timeoutCC = Timer.scheduledTimer(withTimeInterval: 30.0, repeats: false, block: {_ in
                     let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
@@ -2229,7 +2232,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         let idMe = UserDefaults.standard.string(forKey: "me") as String?
         var opposite_pin = ""
         if isContactCenter {
-            opposite_pin = fPinContacCenter
+            opposite_pin = ""
         } else {
             opposite_pin = idMe ?? ""
         }

+ 1 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/ChangeDeviceViewController.swift

@@ -221,7 +221,7 @@ public class ChangeDeviceViewController: UIViewController {
         }
         Nexilis.showLoader()
         DispatchQueue.global().async {
-            let md5Hex = Utils.getMD5(string: password).map { String(format: "%02hhx", $0) }.joined()
+            let md5Hex = password
             if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getSignIn(p_name: name, p_password: md5Hex), timeout: 30 * 1000) {
                 if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "11" {
                     DispatchQueue.main.async {

+ 1 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/ChangeNamePassswordViewController.swift

@@ -147,7 +147,7 @@ public class ChangeNamePassswordViewController: UIViewController {
                         })
                     }
                 } else if resp.isOk() {
-                    let md5Hex = Utils.getMD5(string: password).map { String(format: "%02hhx", $0) }.joined()
+                    let md5Hex = password
                     let tMessage = CoreMessage_TMessageBank.getChangePersonInfo_New(p_f_pin: idMe)
                     tMessage.mBodies[CoreMessage_TMessageKey.FIRST_NAME] = first
                     tMessage.mBodies[CoreMessage_TMessageKey.LAST_NAME] = last

+ 2 - 2
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/ChangePasswordViewController.swift

@@ -84,8 +84,8 @@ class ChangePasswordViewController: UIViewController {
         let idMe = UserDefaults.standard.string(forKey: "me")!
         DispatchQueue.global().async {
             let tMessage = CoreMessage_TMessageBank.getChangePersonInfo_New(p_f_pin: idMe)
-            let md5HexOld = Utils.getMD5(string: oldPassword).map { String(format: "%02hhx", $0) }.joined()
-            let md5HexNew = Utils.getMD5(string: newPassword).map { String(format: "%02hhx", $0) }.joined()
+            let md5HexOld = oldPassword
+            let md5HexNew = newPassword
             tMessage.mBodies[CoreMessage_TMessageKey.PASSWORD] = md5HexNew
             tMessage.mBodies[CoreMessage_TMessageKey.PASSWORD_OLD] = md5HexOld
             if let resp = Nexilis.writeAndWait(message: tMessage){

+ 19 - 2
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/HistoryCCViewController.swift

@@ -14,6 +14,8 @@ public class HistoryCCViewController: UITableViewController, QLPreviewController
     public var isOfficer = false
     
     var previewItem: NSURL?
+    
+    var fromAPI = false
 
     public override func viewDidLoad() {
         super.viewDidLoad()
@@ -21,6 +23,21 @@ public class HistoryCCViewController: UITableViewController, QLPreviewController
         
         self.view.backgroundColor = .white
         
+        if fromAPI {
+            let imageButton = UIImageView(frame: CGRect(x: -16, y: 0, width: 20, height: 44))
+            imageButton.image = UIImage(systemName: "chevron.backward", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .regular, scale: .default))?.withTintColor(.white)
+            imageButton.contentMode = .left
+            let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapExit))
+            imageButton.isUserInteractionEnabled = true
+            imageButton.addGestureRecognizer(tapGestureRecognizer)
+            let leftItem = UIBarButtonItem(customView: imageButton)
+            self.navigationItem.leftBarButtonItem = leftItem
+        }
+        
+    }
+    
+    @objc func didTapExit() {
+        self.dismiss(animated: true, completion: nil)
     }
     
     public override func viewWillAppear(_ animated: Bool) {
@@ -58,11 +75,11 @@ public class HistoryCCViewController: UITableViewController, QLPreviewController
         cell.imageOfficer.image = nil
         if dataOfficer.count > 0 {
             if isOfficer {
-                cell.labelOfficer.text = dataRequester["name"]!
+                cell.labelOfficer.text = dataRequester["name"] ?? ""
             } else {
                 cell.labelOfficer.text = "Officer".localized() + " : " + dataOfficer["name"]!
             }
-            if !(dataOfficer["image"]!).isEmpty || !(dataRequester["image"]!).isEmpty {
+            if !(dataOfficer["image"] ?? "").isEmpty || !(dataRequester["image"] ?? "").isEmpty {
                 if isOfficer {
                     getImage(name: dataRequester["image"]!, placeholderImage: UIImage(systemName: "person.circle.fill")!, isCircle: true, tableView: tableView, indexPath: indexPath) { result, isDownloaded, image in
                         cell.imageOfficer.image = image

+ 4 - 4
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/ProfileViewController.swift

@@ -110,11 +110,11 @@ public class ProfileViewController: UITableViewController {
                         } else if User.isCallCenter(userType: user.userType ?? "") {
                             let dataCategory = CategoryCC.getDataFromServiceId(service_id: user.ex_offmp!)
                             self.imageUserType.image = UIImage(named: "pb_call_center", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
-                            if dataCategory != nil {
-                                self.labelUserType.text = "Call Center (\(dataCategory!.service_name))".localized()
-                            } else {
+//                            if dataCategory != nil {
+//                                self.labelUserType.text = "Call Center (\(dataCategory!.service_name))".localized()
+//                            } else {
                                 self.labelUserType.text = "Call Center".localized()
-                            }
+//                            }
 //                            self.buttonHistoryCC.isHidden = true
                         }
                     }

+ 6 - 6
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/SettingTableViewController.swift

@@ -770,7 +770,7 @@ public class SettingTableViewController: UITableViewController, UIGestureRecogni
         DispatchQueue.global().async {
             let idMe = UserDefaults.standard.string(forKey: "me") as String?
             let p_password = password
-            let md5Hex = Utils.getMD5(string: p_password).map { String(format: "%02hhx", $0) }.joined()
+            let md5Hex = p_password
             var result: Bool = false
             if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getSignInApiAdmin(p_name: idMe!, p_password: md5Hex)) {
                 if response.isOk() {
@@ -805,7 +805,7 @@ public class SettingTableViewController: UITableViewController, UIGestureRecogni
         DispatchQueue.global().async {
             let idMe = UserDefaults.standard.string(forKey: "me") as String?
             let p_password = password
-            let md5Hex = Utils.getMD5(string: p_password).map { String(format: "%02hhx", $0) }.joined()
+            let md5Hex = p_password
             var result: Bool = false
             if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getSignInApiInternal(p_name: idMe!, p_password: md5Hex)) {
                 if response.isOk() {
@@ -841,8 +841,8 @@ public class SettingTableViewController: UITableViewController, UIGestureRecogni
             let idMe = UserDefaults.standard.string(forKey: "me") as String?
             let p_password = oldPassword
             let n_password = newPassword
-            let md5Hex = Utils.getMD5(string: p_password).map { String(format: "%02hhx", $0) }.joined()
-            let md5HexNew = Utils.getMD5(string: n_password).map { String(format: "%02hhx", $0) }.joined()
+            let md5Hex = p_password
+            let md5HexNew = n_password
             var result: Bool = false
             if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getChangePasswordAdmin(p_f_pin: idMe!, pwd_en: md5HexNew, pwd_old: md5Hex)) {
                 if response.isOk() {
@@ -878,8 +878,8 @@ public class SettingTableViewController: UITableViewController, UIGestureRecogni
             let idMe = UserDefaults.standard.string(forKey: "me") as String?
             let p_password = oldPassword
             let n_password = newPassword
-            let md5Hex = Utils.getMD5(string: p_password).map { String(format: "%02hhx", $0) }.joined()
-            let md5HexNew = Utils.getMD5(string: n_password).map { String(format: "%02hhx", $0) }.joined()
+            let md5Hex = p_password
+            let md5HexNew = n_password
             var result: Bool = false
             if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getChangePasswordInternal(p_f_pin: idMe!, pwd_en: md5HexNew, pwd_old: md5Hex)) {
                 if response.isOk() {

+ 1 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/SignUpSignIn.swift

@@ -251,7 +251,7 @@ public class SignUpSignIn: UIViewController {
         }
         Nexilis.showLoader()
         DispatchQueue.global().async {
-            let md5Hex = Utils.getMD5(string: password).map { String(format: "%02hhx", $0) }.joined()
+            let md5Hex = password
             if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getSignUpSignInAPI(p_name: name, p_password: md5Hex), timeout: 30 * 1000) {
                 if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "20" {
                     DispatchQueue.main.async {